Skip to content

Commit

Permalink
Elements. Restore lazy reading ClassElement members.
Browse files Browse the repository at this point in the history
Bug: flutter/flutter#161306
Change-Id: I65b606b8aa012cebf62d866128bc35c5532e5638
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/403924
Reviewed-by: Brian Wilkerson <[email protected]>
Commit-Queue: Konstantin Shcheglov <[email protected]>
  • Loading branch information
scheglov authored and Commit Queue committed Jan 13, 2025
1 parent 519e288 commit 8a3b4ab
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 28 deletions.
2 changes: 1 addition & 1 deletion pkg/analyzer/lib/src/dart/analysis/driver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 17 additions & 1 deletion pkg/analyzer/lib/src/dart/element/element.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down Expand Up @@ -5433,6 +5433,11 @@ abstract class InstanceElementImpl extends _ExistingElementImpl

@override
List<PropertyAccessorElementImpl> get accessors {
if (!identical(_accessors, _Sentinel.propertyAccessorElement)) {
return _accessors;
}

linkedData?.readMembers(this);
return _accessors;
}

Expand Down Expand Up @@ -5462,6 +5467,11 @@ abstract class InstanceElementImpl extends _ExistingElementImpl

@override
List<FieldElementImpl> get fields {
if (!identical(_fields, _Sentinel.fieldElement)) {
return _fields;
}

linkedData?.readMembers(this);
return _fields;
}

Expand Down Expand Up @@ -5490,6 +5500,11 @@ abstract class InstanceElementImpl extends _ExistingElementImpl

@override
List<MethodElementImpl> get methods {
if (!identical(_methods, _Sentinel.methodElement)) {
return _methods;
}

linkedData?.readMembers(this);
return _methods;
}

Expand Down Expand Up @@ -5975,6 +5990,7 @@ abstract class InterfaceElementImpl extends InstanceElementImpl
}

_buildMixinAppConstructors();
linkedData?.readMembers(this);
return _constructors;
}

Expand Down
92 changes: 70 additions & 22 deletions pkg/analyzer/lib/src/summary2/bundle_reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}),
Expand All @@ -94,6 +95,7 @@ class BundleReader {
referenceReader: referenceReader,
reference: reference,
offset: libraryHeader.offset,
classMembersLengths: libraryHeader.classMembersLengths,
infoDeclarationStore: _infoDeclarationStore,
macroGeneratedCode: libraryHeader.macroGeneratedCode,
);
Expand All @@ -103,6 +105,8 @@ class BundleReader {

class ClassElementLinkedData extends ElementLinkedData<ClassElementImpl> {
ApplyConstantOffsets? applyConstantOffsets;
void Function()? _readMembers;
void Function()? applyInformativeDataToMembers;

ClassElementLinkedData({
required Reference reference,
Expand All @@ -112,17 +116,14 @@ class ClassElementLinkedData extends ElementLinkedData<ClassElementImpl> {
}) : 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);
}
}
}

Expand Down Expand Up @@ -154,6 +155,22 @@ class ClassElementLinkedData extends ElementLinkedData<ClassElementImpl> {

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
Expand Down Expand Up @@ -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;

Expand All @@ -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,
Expand All @@ -636,6 +657,7 @@ class LibraryReader {
_referenceReader = referenceReader,
_reference = reference,
_offset = offset,
_classMembersLengths = classMembersLengths,
_deserializedDataStore = infoDeclarationStore;

LibraryElementImpl readElement({required Source librarySource}) {
Expand All @@ -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();
Expand Down Expand Up @@ -754,22 +781,37 @@ class LibraryReader {
fragment.typeParameters = _readTypeParameters();

if (!fragment.isMixinApplication) {
var accessors = <PropertyAccessorElementImpl>[];
var fields = <FieldElementImpl>[];
_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 = <PropertyAccessorElementImpl>[];
var fields = <FieldElementImpl>[];
_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,
Expand Down Expand Up @@ -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,
});
}
Expand Down
12 changes: 12 additions & 0 deletions pkg/analyzer/lib/src/summary2/bundle_writer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<int> _classMembersLengths = [];

final StringIndexer _stringIndexer = StringIndexer();

final List<_Library> _libraries = [];
Expand All @@ -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);
});
Expand All @@ -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);
Expand Down Expand Up @@ -128,6 +135,7 @@ class BundleWriter {
_Library(
uriStr: '${libraryElement.source.uri}',
offset: libraryOffset,
classMembersOffsets: _classMembersLengths,
macroGenerated: macroGenerated,
),
);
Expand Down Expand Up @@ -171,6 +179,7 @@ class BundleWriter {
}

if (!element.isMixinApplication) {
var membersOffset = _sink.offset;
_writeList(
element.fields.where((e) => !e.isSynthetic).toList(),
_writeFieldElement,
Expand All @@ -181,6 +190,7 @@ class BundleWriter {
);
_writeList(element.constructors, _writeConstructorElement);
_writeList(element.methods, _writeMethodElement);
_classMembersLengths.add(_sink.offset - membersOffset);
}
});
}
Expand Down Expand Up @@ -1296,13 +1306,15 @@ class _BundleWriterReferences {
class _Library {
final String uriStr;
final int offset;
final List<int> classMembersOffsets;

/// The only (if any) macro generated fragment.
final MacroGeneratedLibraryFragment? macroGenerated;

_Library({
required this.uriStr,
required this.offset,
required this.classMembersOffsets,
required this.macroGenerated,
});
}
Expand Down
13 changes: 9 additions & 4 deletions pkg/analyzer/lib/src/summary2/informative_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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();
}
}

Expand Down
11 changes: 11 additions & 0 deletions pkg/analyzer/lib/src/utilities/extensions/element.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ extension ClassElementExtension on ClassElement {
}
}

extension ClassElementImpl2Extension on ClassElementImpl2 {
List<ClassElementImpl> get fragments {
return [
for (ClassElementImpl? fragment = firstFragment;
fragment != null;
fragment = fragment.nextFragment)
fragment,
];
}
}

extension CompilationUnitElementExtension on CompilationUnitElement {
LibraryFragment get asElement2 {
return this as LibraryFragment;
Expand Down

0 comments on commit 8a3b4ab

Please sign in to comment.