diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart index 4ff1baa06a79..537185f91b17 100644 --- a/pkg/analyzer/lib/src/dart/analysis/driver.dart +++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart @@ -99,7 +99,7 @@ import 'package:meta/meta.dart'; // TODO(scheglov): Clean up the list of implicitly analyzed files. class AnalysisDriver { /// The version of data format, should be incremented on every format change. - static const int DATA_VERSION = 426; + static const int DATA_VERSION = 427; /// The number of exception contexts allowed to write. Once this field is /// zero, we stop writing any new exception contexts in this process. diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart index d4e5bb810ba9..2a3a50483d54 100644 --- a/pkg/analyzer/lib/src/dart/element/element.dart +++ b/pkg/analyzer/lib/src/dart/element/element.dart @@ -451,7 +451,7 @@ class ClassElementImpl extends ClassOrMixinElementImpl } @override - ClassFragment? get nextFragment => super.nextFragment as ClassFragment?; + ClassElementImpl? get nextFragment => super.nextFragment as ClassElementImpl?; @override ClassFragment? get previousFragment => @@ -5433,6 +5433,11 @@ abstract class InstanceElementImpl extends _ExistingElementImpl @override List get accessors { + if (!identical(_accessors, _Sentinel.propertyAccessorElement)) { + return _accessors; + } + + linkedData?.readMembers(this); return _accessors; } @@ -5462,6 +5467,11 @@ abstract class InstanceElementImpl extends _ExistingElementImpl @override List get fields { + if (!identical(_fields, _Sentinel.fieldElement)) { + return _fields; + } + + linkedData?.readMembers(this); return _fields; } @@ -5490,6 +5500,11 @@ abstract class InstanceElementImpl extends _ExistingElementImpl @override List get methods { + if (!identical(_methods, _Sentinel.methodElement)) { + return _methods; + } + + linkedData?.readMembers(this); return _methods; } @@ -5975,6 +5990,7 @@ abstract class InterfaceElementImpl extends InstanceElementImpl } _buildMixinAppConstructors(); + linkedData?.readMembers(this); return _constructors; } diff --git a/pkg/analyzer/lib/src/summary2/bundle_reader.dart b/pkg/analyzer/lib/src/summary2/bundle_reader.dart index 31ec6d106bf5..68eefaf61cc9 100644 --- a/pkg/analyzer/lib/src/summary2/bundle_reader.dart +++ b/pkg/analyzer/lib/src/summary2/bundle_reader.dart @@ -76,6 +76,7 @@ class BundleReader { return _LibraryHeader( uri: uriCache.parse(_reader.readStringReference()), offset: _reader.readUInt30(), + classMembersLengths: _reader.readUInt30List(), macroGeneratedCode: _reader.readOptionalObject((reader) { return _reader.readStringUtf8(); }), @@ -94,6 +95,7 @@ class BundleReader { referenceReader: referenceReader, reference: reference, offset: libraryHeader.offset, + classMembersLengths: libraryHeader.classMembersLengths, infoDeclarationStore: _infoDeclarationStore, macroGeneratedCode: libraryHeader.macroGeneratedCode, ); @@ -103,6 +105,8 @@ class BundleReader { class ClassElementLinkedData extends ElementLinkedData { ApplyConstantOffsets? applyConstantOffsets; + void Function()? _readMembers; + void Function()? applyInformativeDataToMembers; ClassElementLinkedData({ required Reference reference, @@ -112,17 +116,14 @@ class ClassElementLinkedData extends ElementLinkedData { }) : super(reference, libraryReader, unitElement, offset); @override - void readMembers(InstanceElementImpl element) { - if (element is! ClassElementImpl) { - return; - } - - // We might read class members before other properties. - element.linkedData?.read(element); - element.linkedData = null; - - if (element.isMixinApplication) { - element.constructors; + void readMembers(covariant ClassElementImpl fragment) { + // Read members of all fragments, in order. + // So we always read a method augmentation after its target. + for (var fragment in fragment.element.fragments) { + var linkedData = fragment.linkedData; + if (linkedData is ClassElementLinkedData) { + linkedData._readSingleFragmentMembers(fragment); + } } } @@ -154,6 +155,22 @@ class ClassElementLinkedData extends ElementLinkedData { applyConstantOffsets?.perform(); } + + void _readSingleFragmentMembers(ClassElementImpl element) { + // We might read class members before other properties. + element.linkedData?.read(element); + element.linkedData = null; + + if (element.isMixinApplication) { + element.constructors; + } else { + _readMembers?.call(); + _readMembers = null; + + applyInformativeDataToMembers?.call(); + applyInformativeDataToMembers = null; + } + } } class CompilationUnitElementLinkedData @@ -615,6 +632,9 @@ class LibraryReader { final InfoDeclarationStore _deserializedDataStore; final String? macroGeneratedCode; + final Uint32List _classMembersLengths; + int _classMembersLengthsIndex = 0; + late final LibraryElementImpl _libraryElement; late InstanceElementImpl2 _currentInstanceElement; @@ -627,6 +647,7 @@ class LibraryReader { required _ReferenceReader referenceReader, required Reference reference, required int offset, + required Uint32List classMembersLengths, required InfoDeclarationStore infoDeclarationStore, required this.macroGeneratedCode, }) : _elementFactory = elementFactory, @@ -636,6 +657,7 @@ class LibraryReader { _referenceReader = referenceReader, _reference = reference, _offset = offset, + _classMembersLengths = classMembersLengths, _deserializedDataStore = infoDeclarationStore; LibraryElementImpl readElement({required Source librarySource}) { @@ -644,6 +666,11 @@ class LibraryReader { _reader.offset = _offset; + // TODO(scheglov): https://github.com/dart-lang/sdk/issues/51855 + // This should not be needed. + // But I have a suspicion that we attempt to read the library twice. + _classMembersLengthsIndex = 0; + // Read enough data to create the library. var name = _reader.readStringReference(); var featureSet = _readFeatureSet(); @@ -754,22 +781,37 @@ class LibraryReader { fragment.typeParameters = _readTypeParameters(); if (!fragment.isMixinApplication) { - var accessors = []; - var fields = []; - _readFields(unitElement, fragment, reference, accessors, fields); - _readPropertyAccessors( - unitElement, fragment, reference, accessors, fields, '@field'); - fragment.fields = fields.toFixedList(); - fragment.accessors = accessors.toFixedList(); - - fragment.constructors = - _readConstructors(unitElement, fragment, reference); - fragment.methods = _readMethods(unitElement, fragment, reference); + var membersOffset = _reader.offset; + linkedData._readMembers = () { + _reader.offset = membersOffset; + _readClassElementMembers(fragment, reference); + }; + _reader.offset += _classMembersLengths[_classMembersLengthsIndex++]; } return fragment; } + void _readClassElementMembers( + ClassElementImpl fragment, + Reference reference, + ) { + // print('[_readClassElementMembers][reference: $reference]'); + var unitElement = fragment.enclosingElement3; + _currentInstanceElement = fragment.element; + + var accessors = []; + var fields = []; + _readFields(unitElement, fragment, reference, accessors, fields); + _readPropertyAccessors( + unitElement, fragment, reference, accessors, fields, '@field'); + fragment.fields = fields.toFixedList(); + fragment.accessors = accessors.toFixedList(); + + fragment.constructors = _readConstructors(unitElement, fragment, reference); + fragment.methods = _readMethods(unitElement, fragment, reference); + } + void _readClasses( CompilationUnitElementImpl unitElement, Reference unitReference, @@ -2665,12 +2707,18 @@ class _LibraryHeader { final Uri uri; final int offset; + /// We don't read class members when reading libraries, by performance + /// reasons - in many cases only some classes of a library are used. But + /// we need to know how much data to skip for each class. + final Uint32List classMembersLengths; + /// The only (if any) macro generated augmentation code. final String? macroGeneratedCode; _LibraryHeader({ required this.uri, required this.offset, + required this.classMembersLengths, required this.macroGeneratedCode, }); } diff --git a/pkg/analyzer/lib/src/summary2/bundle_writer.dart b/pkg/analyzer/lib/src/summary2/bundle_writer.dart index c22e67c2f785..5141b468be4d 100644 --- a/pkg/analyzer/lib/src/summary2/bundle_writer.dart +++ b/pkg/analyzer/lib/src/summary2/bundle_writer.dart @@ -57,6 +57,11 @@ class BundleWriter { references: _references, ); + /// [_writeClassElement] remembers the length of data written into [_sink] + /// while writing members. So, when we read, we can skip members initially, + /// and read them later on demand. + List _classMembersLengths = []; + final StringIndexer _stringIndexer = StringIndexer(); final List<_Library> _libraries = []; @@ -73,6 +78,7 @@ class BundleWriter { _sink.writeList<_Library>(_libraries, (library) { _sink._writeStringReference(library.uriStr); _sink.writeUInt30(library.offset); + _sink.writeUint30List(library.classMembersOffsets); _sink.writeOptionalObject(library.macroGenerated, (it) { _sink.writeStringUtf8(it.code); }); @@ -99,6 +105,7 @@ class BundleWriter { void writeLibraryElement(LibraryElementImpl libraryElement) { var libraryOffset = _sink.offset; + _classMembersLengths = []; // Write non-resolution data for the library. _sink._writeStringReference(libraryElement.name); @@ -128,6 +135,7 @@ class BundleWriter { _Library( uriStr: '${libraryElement.source.uri}', offset: libraryOffset, + classMembersOffsets: _classMembersLengths, macroGenerated: macroGenerated, ), ); @@ -171,6 +179,7 @@ class BundleWriter { } if (!element.isMixinApplication) { + var membersOffset = _sink.offset; _writeList( element.fields.where((e) => !e.isSynthetic).toList(), _writeFieldElement, @@ -181,6 +190,7 @@ class BundleWriter { ); _writeList(element.constructors, _writeConstructorElement); _writeList(element.methods, _writeMethodElement); + _classMembersLengths.add(_sink.offset - membersOffset); } }); } @@ -1296,6 +1306,7 @@ class _BundleWriterReferences { class _Library { final String uriStr; final int offset; + final List classMembersOffsets; /// The only (if any) macro generated fragment. final MacroGeneratedLibraryFragment? macroGenerated; @@ -1303,6 +1314,7 @@ class _Library { _Library({ required this.uriStr, required this.offset, + required this.classMembersOffsets, required this.macroGenerated, }); } diff --git a/pkg/analyzer/lib/src/summary2/informative_data.dart b/pkg/analyzer/lib/src/summary2/informative_data.dart index ba15fabbfc43..32595940e861 100644 --- a/pkg/analyzer/lib/src/summary2/informative_data.dart +++ b/pkg/analyzer/lib/src/summary2/informative_data.dart @@ -227,10 +227,6 @@ class InformativeDataApplier { element.typeParameters_unresolved, info.typeParameters, ); - _applyToConstructors(element.constructors, info.constructors); - _applyToFields(element.fields, info.fields); - _applyToAccessors(element.accessors, info.accessors); - _applyToMethods(element.methods, info.methods); var applyOffsets = ApplyConstantOffsets( info.constantOffsets, @@ -240,11 +236,20 @@ class InformativeDataApplier { }, ); + void applyToMembers() { + _applyToConstructors(element.constructors, info.constructors); + _applyToFields(element.fields, info.fields); + _applyToAccessors(element.accessors, info.accessors); + _applyToMethods(element.methods, info.methods); + } + var linkedData = element.linkedData; if (linkedData is ClassElementLinkedData) { linkedData.applyConstantOffsets = applyOffsets; + linkedData.applyInformativeDataToMembers = applyToMembers; } else { applyOffsets.perform(); + applyToMembers(); } } diff --git a/pkg/analyzer/lib/src/utilities/extensions/element.dart b/pkg/analyzer/lib/src/utilities/extensions/element.dart index 0980624c1288..d234639f6a4a 100644 --- a/pkg/analyzer/lib/src/utilities/extensions/element.dart +++ b/pkg/analyzer/lib/src/utilities/extensions/element.dart @@ -25,6 +25,17 @@ extension ClassElementExtension on ClassElement { } } +extension ClassElementImpl2Extension on ClassElementImpl2 { + List get fragments { + return [ + for (ClassElementImpl? fragment = firstFragment; + fragment != null; + fragment = fragment.nextFragment) + fragment, + ]; + } +} + extension CompilationUnitElementExtension on CompilationUnitElement { LibraryFragment get asElement2 { return this as LibraryFragment;