blob: 38481dc72bbca217761335b6d01c73572148dbda [file] [log] [blame]
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
/// Cached data about the extensions defined in a single analysis context.
class ExtensionCache {
/// A set containing the paths of the compilation units that have been cached.
/// The set is used to prevent caching the same data multiple times.
final Set<String> processedUnits = {};
/// A map from the name of a non-static public extension member to the set of
/// paths to libraries defining an extension member with that name.
final Map<String, Set<UnitInLibrary>> membersByName = {};
/// Initialize a newly created cache.
ExtensionCache();
/// Fill the cache with data from the [result].
void cacheFromResult(ResolvedUnitResult result) {
var element = result.unit.declaredElement;
if (element != null) {
_cacheFromElement(element);
for (var library in result.libraryElement.importedLibraries) {
_cacheLibrary(library);
}
}
}
/// Fill the cache with data from the [compilationUnit].
void _cacheFromElement(CompilationUnitElement compilationUnit) {
// Record that we've cached data for the compilation unit.
var unitPath = _keyForUnit(compilationUnit);
processedUnits.add(unitPath);
// Flush any data that was previously cached for the compilation unit.
for (var set in membersByName.values) {
set.removeWhere((element) => element.unitPath == unitPath);
}
// Cache the data for the compilation unit.
var libraryPath = compilationUnit.librarySource.fullName;
for (var extension in compilationUnit.extensions) {
var extensionName = extension.name;
if (extensionName != null && !Identifier.isPrivateName(extensionName)) {
for (var member in extension.accessors) {
if (!member.isSynthetic) {
_recordMember(unitPath, libraryPath, member.displayName);
}
}
for (var member in extension.fields) {
if (!member.isSynthetic) {
_recordMember(unitPath, libraryPath, member.name);
}
}
for (var member in extension.methods) {
_recordMember(unitPath, libraryPath, member.name);
}
}
}
}
/// Cache the data for the given [library] and every library exported from it
/// if it hasn't already been cached.
void _cacheLibrary(LibraryElement library) {
if (_hasDataFor(library.definingCompilationUnit)) {
return;
}
for (var unit in library.units) {
_cacheFromElement(unit);
}
for (var exported in library.exportedLibraries) {
_cacheLibrary(exported);
}
}
/// Return `true` if the cache contains data for the [compilationUnit].
bool _hasDataFor(CompilationUnitElement compilationUnit) {
return processedUnits.contains(_keyForUnit(compilationUnit));
}
/// Return the key used in the [extensionCache] for the [compilationUnit].
String _keyForUnit(CompilationUnitElement compilationUnit) =>
compilationUnit.source.fullName;
/// Record that an extension member with the given [name] is defined in the
/// compilation unit with the [unitPath] in the library with the
/// [libraryPath].
void _recordMember(String unitPath, String libraryPath, String name) {
membersByName
.putIfAbsent(name, () => {})
.add(UnitInLibrary(unitPath, libraryPath));
}
}
/// A representation of a compilation unit in a library.
class UnitInLibrary {
final String unitPath;
final String libraryPath;
UnitInLibrary(this.unitPath, this.libraryPath);
}