Skip to content

Commit

Permalink
[analyzer] Remove the [ImportedElement] class
Browse files Browse the repository at this point in the history
When running analyzer on flutter and it's transitive sources, it
consumes around 480 MB of memory.

The [ImportedElement] class is an intermediatey wrapper around an
[Element] and a boolean that is mostly false.

This CL removes the [ImportedElement] class and stores the boolean
side-information alongside the element in [PrefixScope] and
[PrefixScopeLookupResult].

This saves around 15.5 MB of memory.

TEST=ci

Change-Id: I94692a9709c70decd90c604bbdc85f5b308b3006
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/256029
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
  • Loading branch information
mkustermann authored and Commit Bot committed Aug 24, 2022
1 parent bba453b commit 4193bb3
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 71 deletions.
119 changes: 63 additions & 56 deletions pkg/analyzer/lib/src/dart/element/scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,6 @@ class FormalParameterScope extends EnclosedScope {
}
}

class ImportedElement {
final Element element;

/// This flag is set to `true` if [element] is available using import
/// directives where every imported library re-exports the element, and
/// every such `export` directive is marked as deprecated.
final bool isFromDeprecatedExport;

ImportedElement({
required this.element,
required this.isFromDeprecatedExport,
});
}

/// The scope defined by an interface element.
class InterfaceScope extends EnclosedScope {
InterfaceScope(super.parent, InterfaceElement element) {
Expand Down Expand Up @@ -158,8 +144,10 @@ class LocalScope extends EnclosedScope {

class PrefixScope implements Scope {
final LibraryOrAugmentationElementImpl _container;
final Map<String, ImportedElement> _getters = {};
final Map<String, ImportedElement> _setters = {};
final Map<String, Element> _getters = {};
final Map<String, Element> _setters = {};
Set<String>? _settersFromDeprecatedExport;
Set<String>? _gettersFromDeprecatedExport;
final Set<ExtensionElement> _extensions = {};
LibraryElement? _deferredLibrary;

Expand All @@ -176,12 +164,8 @@ class PrefixScope implements Scope {
final reference = exportedReference.reference;
if (combinators.allows(reference.name)) {
final element = elementFactory.elementOfReference(reference)!;
final importedElement = ImportedElement(
element: element,
isFromDeprecatedExport:
_isFromDeprecatedExport(importedLibrary, exportedReference),
);
_add(importedElement);
_add(element,
_isFromDeprecatedExport(importedLibrary, exportedReference));
}
}
if (import.prefix is DeferredImportElementPrefix) {
Expand All @@ -199,48 +183,56 @@ class PrefixScope implements Scope {
return ScopeLookupResultImpl(deferredLibrary.loadLibraryFunction, null);
}

var getter = _getters[id];
var setter = _setters[id];
return PrefixScopeLookupResult(getter, setter);
return PrefixScopeLookupResult(
_getters[id],
_setters[id],
_gettersFromDeprecatedExport?.contains(id) ?? false,
_settersFromDeprecatedExport?.contains(id) ?? false);
}

void _add(ImportedElement imported) {
final element = imported.element;
void _add(Element element, bool isFromDeprecatedExport) {
if (element is PropertyAccessorElement && element.isSetter) {
_addTo(map: _setters, incoming: imported);
_addTo(element, isFromDeprecatedExport, isSetter: true);
} else {
_addTo(map: _getters, incoming: imported);
_addTo(element, isFromDeprecatedExport, isSetter: false);
if (element is ExtensionElement) {
_extensions.add(element);
}
}
}

void _addTo({
required Map<String, ImportedElement> map,
required ImportedElement incoming,
}) {
final id = incoming.element.displayName;
void _addTo(Element element, bool isDeprecatedExport,
{required bool isSetter}) {
final map = isSetter ? _setters : _getters;
final id = element.displayName;
final existing = map[id];

if (existing == null) {
map[id] = incoming;
map[id] = element;
if (isDeprecatedExport) {
if (isSetter) {
(_settersFromDeprecatedExport ??= {}).add(id);
} else {
(_gettersFromDeprecatedExport ??= {}).add(id);
}
}
return;
}

if (existing.element == incoming.element) {
map[id] = ImportedElement(
element: incoming.element,
isFromDeprecatedExport:
existing.isFromDeprecatedExport && incoming.isFromDeprecatedExport,
);
final deprecatedSet =
isSetter ? _settersFromDeprecatedExport : _gettersFromDeprecatedExport;
final wasFromDeprecatedExport = deprecatedSet?.contains(id) ?? false;
if (existing == element) {
if (wasFromDeprecatedExport && !isDeprecatedExport) {
deprecatedSet!.remove(id);
}
return;
}

map[id] = ImportedElement(
element: _merge(existing.element, incoming.element),
isFromDeprecatedExport: false,
);
map[id] = _merge(existing, element);
if (wasFromDeprecatedExport) {
deprecatedSet!.remove(id);
}
}

Element _merge(Element existing, Element other) {
Expand Down Expand Up @@ -305,20 +297,35 @@ class PrefixScope implements Scope {
}
}

class PrefixScopeLookupResult implements ScopeLookupResult {
final ImportedElement? importedGetter;
final ImportedElement? importedSetter;
class PrefixScopeLookupResult extends ScopeLookupResultImpl {
static const int getterIsFromDeprecatedExportBit = 1 << 0;
static const int setterIsFromDeprecatedExportBit = 1 << 1;

PrefixScopeLookupResult(
this.importedGetter,
this.importedSetter,
);
final int _deprecatedBits;

@override
Element? get getter => importedGetter?.element;
PrefixScopeLookupResult(
super.importedGetter,
super.importedSetter,
bool getterIsFromDeprecatedExport,
bool setterIsFromDeprecatedExport,
) : _deprecatedBits = (getterIsFromDeprecatedExport
? getterIsFromDeprecatedExportBit
: 0) |
(setterIsFromDeprecatedExport
? setterIsFromDeprecatedExportBit
: 0);

/// This flag is set to `true` if [getter] is available using import
/// directives where every imported library re-exports the element, and
/// every such `export` directive is marked as deprecated.
bool get getterIsFromDeprecatedExport =>
(_deprecatedBits & getterIsFromDeprecatedExportBit) != 0;

@override
Element? get setter => importedSetter?.element;
/// This flag is set to `true` if [setter] is available using import
/// directives where every imported library re-exports the element, and
/// every such `export` directive is marked as deprecated.
bool get setterIsFromDeprecatedExport =>
(_deprecatedBits & setterIsFromDeprecatedExportBit) != 0;
}

class ScopeLookupResultImpl implements ScopeLookupResult {
Expand Down
25 changes: 10 additions & 15 deletions pkg/analyzer/lib/src/generated/scope_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ mixin ScopeHelpers {
required ScopeLookupResult scopeLookupResult,
required SimpleIdentifier node,
}) {
if (scopeLookupResult is PrefixScopeLookupResult) {
if (scopeLookupResult is PrefixScopeLookupResult &&
scopeLookupResult.getterIsFromDeprecatedExport) {
_reportDeprecatedExportUse(
node: node,
imported: scopeLookupResult.importedGetter,
);
}
}
Expand All @@ -51,24 +51,19 @@ mixin ScopeHelpers {
required ScopeLookupResult scopeLookupResult,
required SimpleIdentifier node,
}) {
if (scopeLookupResult is PrefixScopeLookupResult) {
if (scopeLookupResult is PrefixScopeLookupResult &&
scopeLookupResult.setterIsFromDeprecatedExport) {
_reportDeprecatedExportUse(
node: node,
imported: scopeLookupResult.importedSetter,
);
}
}

void _reportDeprecatedExportUse({
required SimpleIdentifier node,
required final ImportedElement? imported,
}) {
if (imported != null && imported.isFromDeprecatedExport) {
errorReporter.reportErrorForNode(
HintCode.DEPRECATED_EXPORT_USE,
node,
[node.name],
);
}
void _reportDeprecatedExportUse({required SimpleIdentifier node}) {
errorReporter.reportErrorForNode(
HintCode.DEPRECATED_EXPORT_USE,
node,
[node.name],
);
}
}

0 comments on commit 4193bb3

Please sign in to comment.