Fine. Record InstanceElement.methods access.
Change-Id: I6451b6d3f1bed1624f3d9e3c8e0549615b716678 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/430940 Reviewed-by: Paul Berry <paulberry@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
03515b472e
commit
f3a4e5bc54
@@ -4535,7 +4535,10 @@ abstract class InstanceElementImpl2 extends ElementImpl2
|
||||
MetadataImpl get metadata2 => firstFragment.metadata2;
|
||||
|
||||
@override
|
||||
@trackedDirectlyExpensive
|
||||
List<MethodElementImpl2> get methods {
|
||||
globalResultRequirements?.record_instanceElement_methods(element: this);
|
||||
|
||||
return firstFragment.methods.map((e) => e.asElement2).toList();
|
||||
}
|
||||
|
||||
@@ -4613,7 +4616,15 @@ abstract class InstanceElementImpl2 extends ElementImpl2
|
||||
name: name,
|
||||
);
|
||||
|
||||
return methods.firstWhereOrNull((e) => e.lookupName == name);
|
||||
return globalResultRequirements.withoutRecording(
|
||||
reason: r'''
|
||||
The result depends only on the requested method, which we have already
|
||||
recorded above.
|
||||
''',
|
||||
operation: () {
|
||||
return methods.firstWhereOrNull((e) => e.lookupName == name);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@Deprecated('Use getMethod instead')
|
||||
|
||||
@@ -82,6 +82,10 @@ class ManifestItemIdList {
|
||||
const ListEquality<ManifestItemId>().equals(other.ids, ids);
|
||||
}
|
||||
|
||||
bool equalToIterable(Iterable<ManifestItemId> other) {
|
||||
return const IterableEquality<ManifestItemId>().equals(ids, other);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '[${ids.join(', ')}]';
|
||||
@@ -90,12 +94,22 @@ class ManifestItemIdList {
|
||||
void write(BufferedSink sink) {
|
||||
sink.writeList(ids, (id) => id.write(sink));
|
||||
}
|
||||
|
||||
static ManifestItemIdList? readOptional(SummaryDataReader reader) {
|
||||
return reader.readOptionalObject(() => ManifestItemIdList.read(reader));
|
||||
}
|
||||
}
|
||||
|
||||
extension ManifestItemIdExtension on ManifestItemId? {
|
||||
void writeOptional(BufferedSink sink) {
|
||||
sink.writeOptionalObject(this, (id) {
|
||||
id.write(sink);
|
||||
sink.writeOptionalObject(this, (it) {
|
||||
it.write(sink);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension ManifestItemIdListOrNullExtension on ManifestItemIdList? {
|
||||
void writeOptional(BufferedSink sink) {
|
||||
sink.writeOptionalObject(this, (it) => it.write(sink));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,20 @@ class InstanceMethodIdMismatch extends RequirementFailure {
|
||||
});
|
||||
}
|
||||
|
||||
class InstanceMethodIdsMismatch extends RequirementFailure {
|
||||
final Uri libraryUri;
|
||||
final LookupName interfaceName;
|
||||
final ManifestItemIdList expectedIds;
|
||||
final ManifestItemIdList actualIds;
|
||||
|
||||
InstanceMethodIdsMismatch({
|
||||
required this.libraryUri,
|
||||
required this.interfaceName,
|
||||
required this.expectedIds,
|
||||
required this.actualIds,
|
||||
});
|
||||
}
|
||||
|
||||
class InterfaceConstructorIdMismatch extends RequirementFailure {
|
||||
final Uri libraryUri;
|
||||
final LookupName interfaceName;
|
||||
|
||||
@@ -202,11 +202,14 @@ class InstanceItemRequirements {
|
||||
final Map<LookupName, ManifestItemId?> requestedSetters;
|
||||
final Map<LookupName, ManifestItemId?> requestedMethods;
|
||||
|
||||
ManifestItemIdList? allDeclaredMethods;
|
||||
|
||||
InstanceItemRequirements({
|
||||
required this.requestedFields,
|
||||
required this.requestedGetters,
|
||||
required this.requestedSetters,
|
||||
required this.requestedMethods,
|
||||
required this.allDeclaredMethods,
|
||||
});
|
||||
|
||||
factory InstanceItemRequirements.empty() {
|
||||
@@ -215,6 +218,7 @@ class InstanceItemRequirements {
|
||||
requestedGetters: {},
|
||||
requestedSetters: {},
|
||||
requestedMethods: {},
|
||||
allDeclaredMethods: null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -224,6 +228,7 @@ class InstanceItemRequirements {
|
||||
requestedGetters: reader.readNameToIdMap(),
|
||||
requestedSetters: reader.readNameToIdMap(),
|
||||
requestedMethods: reader.readNameToIdMap(),
|
||||
allDeclaredMethods: ManifestItemIdList.readOptional(reader),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -232,6 +237,7 @@ class InstanceItemRequirements {
|
||||
sink.writeNameToIdMap(requestedGetters);
|
||||
sink.writeNameToIdMap(requestedSetters);
|
||||
sink.writeNameToIdMap(requestedMethods);
|
||||
allDeclaredMethods.writeOptional(sink);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,6 +327,8 @@ class RequirementsManifest {
|
||||
|
||||
final List<LibraryExportRequirements> exportRequirements = [];
|
||||
|
||||
int _recordingLockLevel = 0;
|
||||
|
||||
RequirementsManifest();
|
||||
|
||||
factory RequirementsManifest.read(SummaryDataReader reader) {
|
||||
@@ -491,6 +499,19 @@ class RequirementsManifest {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (requirements.allDeclaredMethods case var required?) {
|
||||
var actualItems = instanceItem.declaredMethods.values;
|
||||
var actualIds = actualItems.map((item) => item.id);
|
||||
if (!required.equalToIterable(actualIds)) {
|
||||
return InstanceMethodIdsMismatch(
|
||||
libraryUri: libraryUri,
|
||||
interfaceName: instanceName,
|
||||
expectedIds: required,
|
||||
actualIds: ManifestItemIdList(actualIds.toList()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,6 +687,24 @@ class RequirementsManifest {
|
||||
requirements.requestedSetters[methodName] = methodId;
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Record that a member with [nameObj] was requested from the interface
|
||||
/// of [element]. The [methodElement] is used for consistency checking.
|
||||
void record_interface_getMember({
|
||||
@@ -973,6 +1012,25 @@ class _InterfaceItemWithRequirements {
|
||||
});
|
||||
}
|
||||
|
||||
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(
|
||||
|
||||
@@ -6654,6 +6654,8 @@ class B extends A {
|
||||
A: #M0
|
||||
named: <null>
|
||||
instances
|
||||
package:test/a.dart
|
||||
A
|
||||
package:test/test.dart
|
||||
B
|
||||
requestedMethods
|
||||
@@ -6734,6 +6736,8 @@ class A {
|
||||
A: #M0
|
||||
named: <null>
|
||||
instances
|
||||
package:test/a.dart
|
||||
A
|
||||
package:test/test.dart
|
||||
B
|
||||
requestedMethods
|
||||
@@ -7832,6 +7836,131 @@ class A {
|
||||
);
|
||||
}
|
||||
|
||||
test_dependency_class_declared_methods_add() async {
|
||||
_ManualRequirements.install((state) {
|
||||
var A = state.singleUnit.scopeInstanceElement('A');
|
||||
A.methods;
|
||||
});
|
||||
|
||||
await _runChangeScenarioTA(
|
||||
initialA: r'''
|
||||
class A {
|
||||
void foo() {}
|
||||
}
|
||||
''',
|
||||
testCode: r'''
|
||||
import 'a.dart';
|
||||
''',
|
||||
operation: _FineOperationTestFileGetErrors(),
|
||||
expectedInitialEvents: r'''
|
||||
[status] working
|
||||
[operation] linkLibraryCycle SDK
|
||||
[future] getErrors T1
|
||||
ErrorsResult #0
|
||||
path: /home/test/lib/test.dart
|
||||
uri: package:test/test.dart
|
||||
flags: isLibrary
|
||||
errors
|
||||
7 +8 UNUSED_IMPORT
|
||||
[operation] linkLibraryCycle
|
||||
package:test/a.dart
|
||||
declaredClasses
|
||||
A: #M0
|
||||
declaredMethods
|
||||
foo: #M1
|
||||
interface
|
||||
map
|
||||
foo: #M1
|
||||
requirements
|
||||
[operation] linkLibraryCycle
|
||||
package:test/test.dart
|
||||
requirements
|
||||
[operation] analyzeFile
|
||||
file: /home/test/lib/test.dart
|
||||
library: /home/test/lib/test.dart
|
||||
[stream]
|
||||
ResolvedUnitResult #1
|
||||
path: /home/test/lib/test.dart
|
||||
uri: package:test/test.dart
|
||||
flags: exists isLibrary
|
||||
errors
|
||||
7 +8 UNUSED_IMPORT
|
||||
[operation] analyzedLibrary
|
||||
file: /home/test/lib/test.dart
|
||||
requirements
|
||||
topLevels
|
||||
dart:core
|
||||
A: <null>
|
||||
package:test/a.dart
|
||||
A: #M0
|
||||
instances
|
||||
package:test/a.dart
|
||||
A
|
||||
allDeclaredMethods: #M1
|
||||
[status] idle
|
||||
''',
|
||||
updatedA: r'''
|
||||
class A {
|
||||
void foo() {}
|
||||
void bar() {}
|
||||
}
|
||||
''',
|
||||
expectedUpdatedEvents: r'''
|
||||
[status] working
|
||||
[operation] linkLibraryCycle
|
||||
package:test/a.dart
|
||||
declaredClasses
|
||||
A: #M0
|
||||
declaredMethods
|
||||
bar: #M2
|
||||
foo: #M1
|
||||
interface
|
||||
map
|
||||
bar: #M2
|
||||
foo: #M1
|
||||
requirements
|
||||
[future] getErrors T2
|
||||
ErrorsResult #2
|
||||
path: /home/test/lib/test.dart
|
||||
uri: package:test/test.dart
|
||||
flags: isLibrary
|
||||
errors
|
||||
7 +8 UNUSED_IMPORT
|
||||
[operation] readLibraryCycleBundle
|
||||
package:test/test.dart
|
||||
[operation] getErrorsCannotReuse
|
||||
instanceMethodIdsMismatch
|
||||
libraryUri: package:test/a.dart
|
||||
interfaceName: A
|
||||
expectedIds: #M1
|
||||
actualIds: #M1 #M2
|
||||
[operation] analyzeFile
|
||||
file: /home/test/lib/test.dart
|
||||
library: /home/test/lib/test.dart
|
||||
[stream]
|
||||
ResolvedUnitResult #3
|
||||
path: /home/test/lib/test.dart
|
||||
uri: package:test/test.dart
|
||||
flags: exists isLibrary
|
||||
errors
|
||||
7 +8 UNUSED_IMPORT
|
||||
[operation] analyzedLibrary
|
||||
file: /home/test/lib/test.dart
|
||||
requirements
|
||||
topLevels
|
||||
dart:core
|
||||
A: <null>
|
||||
package:test/a.dart
|
||||
A: #M0
|
||||
instances
|
||||
package:test/a.dart
|
||||
A
|
||||
allDeclaredMethods: #M1 #M2
|
||||
[status] idle
|
||||
''',
|
||||
);
|
||||
}
|
||||
|
||||
test_dependency_class_declared_setter() async {
|
||||
_ManualRequirements.install((state) {
|
||||
var A = state.singleUnit.scopeInstanceElement('A');
|
||||
@@ -10301,6 +10430,7 @@ void f (B b) {
|
||||
A
|
||||
requestedMethods
|
||||
noSuchMethod: <null>
|
||||
allDeclaredMethods: #M1
|
||||
B
|
||||
requestedMethods
|
||||
noSuchMethod: <null>
|
||||
@@ -10356,6 +10486,7 @@ void f (B b) {
|
||||
A
|
||||
requestedMethods
|
||||
noSuchMethod: <null>
|
||||
allDeclaredMethods: #M5
|
||||
B
|
||||
requestedMethods
|
||||
noSuchMethod: <null>
|
||||
@@ -10422,6 +10553,7 @@ void f(B b) {
|
||||
A
|
||||
requestedMethods
|
||||
noSuchMethod: <null>
|
||||
allDeclaredMethods: #M1
|
||||
B
|
||||
requestedMethods
|
||||
noSuchMethod: <null>
|
||||
@@ -10475,6 +10607,7 @@ void f(B b) {
|
||||
A
|
||||
requestedMethods
|
||||
noSuchMethod: <null>
|
||||
allDeclaredMethods: #M5
|
||||
B
|
||||
requestedMethods
|
||||
noSuchMethod: <null>
|
||||
@@ -10552,6 +10685,7 @@ void f(B b) {
|
||||
A
|
||||
requestedMethods
|
||||
noSuchMethod: <null>
|
||||
allDeclaredMethods: #M2
|
||||
[status] idle
|
||||
''',
|
||||
updateFiles: () {
|
||||
@@ -10619,6 +10753,7 @@ void f(B b) {
|
||||
requestedMethods
|
||||
_foo: <null>
|
||||
noSuchMethod: <null>
|
||||
allDeclaredMethods: #M4
|
||||
[status] idle
|
||||
''',
|
||||
);
|
||||
@@ -44807,6 +44942,56 @@ import 'a.dart';
|
||||
);
|
||||
}
|
||||
|
||||
test_req_instanceElement_methods() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
class A {
|
||||
static int foo() {}
|
||||
}
|
||||
''');
|
||||
|
||||
newFile(testFile.path, r'''
|
||||
import 'a.dart';
|
||||
''');
|
||||
|
||||
_ManualRequirements.install((state) {
|
||||
var A = state.singleUnit.scopeInstanceElement('A');
|
||||
A.methods;
|
||||
});
|
||||
|
||||
await _runManualRequirementsRecording(
|
||||
expectedEvents: r'''
|
||||
[status] working
|
||||
[operation] linkLibraryCycle SDK
|
||||
[operation] linkLibraryCycle
|
||||
package:test/a.dart
|
||||
declaredClasses
|
||||
A: #M0
|
||||
declaredMethods
|
||||
foo: #M1
|
||||
requirements
|
||||
topLevels
|
||||
dart:core
|
||||
int: #M2
|
||||
[operation] linkLibraryCycle
|
||||
package:test/test.dart
|
||||
requirements
|
||||
[operation] analyzedLibrary
|
||||
file: /home/test/lib/test.dart
|
||||
requirements
|
||||
topLevels
|
||||
dart:core
|
||||
A: <null>
|
||||
package:test/a.dart
|
||||
A: #M0
|
||||
instances
|
||||
package:test/a.dart
|
||||
A
|
||||
allDeclaredMethods: #M1
|
||||
[status] idle
|
||||
''',
|
||||
);
|
||||
}
|
||||
|
||||
test_req_instanceElement_setterElement_variable() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
class A {
|
||||
|
||||
@@ -140,6 +140,16 @@ class BundleRequirementsPrinter {
|
||||
_writeNamedId,
|
||||
);
|
||||
});
|
||||
|
||||
sink.withIndent(() {
|
||||
var idList = instanceEntry.value.allDeclaredMethods;
|
||||
if (idList != null) {
|
||||
if (idList.ids.isNotEmpty) {
|
||||
var idListStr = idList.asString(idProvider);
|
||||
sink.writelnWithIndent('allDeclaredMethods: $idListStr');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -486,6 +496,14 @@ class DriverEventsPrinter {
|
||||
'expectedId': idProvider.manifestId(failure.expectedId),
|
||||
'actualId': idProvider.manifestId(failure.actualId),
|
||||
});
|
||||
case InstanceMethodIdsMismatch():
|
||||
sink.writelnWithIndent('instanceMethodIdsMismatch');
|
||||
sink.writeProperties({
|
||||
'libraryUri': failure.libraryUri,
|
||||
'interfaceName': failure.interfaceName.asString,
|
||||
'expectedIds': failure.expectedIds.asString(idProvider),
|
||||
'actualIds': failure.actualIds.asString(idProvider),
|
||||
});
|
||||
case InterfaceConstructorIdMismatch():
|
||||
sink.writelnWithIndent('interfaceConstructorIdMismatch');
|
||||
sink.writeProperties({
|
||||
@@ -1582,6 +1600,12 @@ class UnitElementPrinterConfiguration {
|
||||
List<Element> Function(LibraryFragment) elementSelector = (_) => [];
|
||||
}
|
||||
|
||||
extension on ManifestItemIdList {
|
||||
String asString(IdProvider idProvider) {
|
||||
return ids.map((id) => idProvider.manifestId(id)).join(' ');
|
||||
}
|
||||
}
|
||||
|
||||
extension on LibraryCycle {
|
||||
bool get isSdk {
|
||||
return libraries.any((library) => library.file.uri.isScheme('dart'));
|
||||
|
||||
Reference in New Issue
Block a user