blob: ad94d7ab191ed6d25a8901609380408a01e82af6 [file] [log] [blame]
// Copyright (c) 2019, 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 'dart:collection';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/context/context.dart';
import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/resolver/scope.dart';
import 'package:analyzer/src/fine/library_manifest.dart';
import 'package:analyzer/src/summary2/bundle_reader.dart';
import 'package:analyzer/src/summary2/export.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/utilities/uri_cache.dart';
import 'package:meta/meta.dart';
final _logRing = Queue<String>();
void addToLogRing(String entry) {
_logRing.add(entry);
if (_logRing.length > 10) {
_logRing.removeFirst();
}
}
class LinkedElementFactory {
static final _dartCoreUri = uriCache.parse('dart:core');
static final _dartAsyncUri = uriCache.parse('dart:async');
final AnalysisContextImpl analysisContext;
AnalysisSessionImpl analysisSession;
final Reference rootReference;
final Map<Uri, LibraryReader> _libraryReaders = {};
bool isApplyingInformativeData = false;
final Map<Uri, LibraryManifest> libraryManifests = {};
LinkedElementFactory(
this.analysisContext,
this.analysisSession,
this.rootReference,
) {
ArgumentError.checkNotNull(analysisContext, 'analysisContext');
ArgumentError.checkNotNull(analysisSession, 'analysisSession');
}
LibraryElementImpl get dartAsyncElement {
return libraryOfUri2(_dartAsyncUri);
}
LibraryElementImpl get dartCoreElement {
return libraryOfUri2(_dartCoreUri);
}
Reference get dynamicRef {
return rootReference.getChild('dart:core').getChild('dynamic');
}
/// Returns URIs for which [LibraryElementImpl] is ready.
@visibleForTesting
List<Uri> get uriListWithLibraryElements {
return rootReference.children
.map((reference) => reference.element)
.whereType<LibraryElementImpl>()
.map((e) => e.uri)
.toList();
}
/// Returns URIs for which we have readers, but not elements.
@visibleForTesting
List<Uri> get uriListWithLibraryReaders {
return _libraryReaders.keys.toList();
}
void addBundle(BundleReader bundle) {
addLibraries(bundle.libraryMap);
}
void addLibraries(Map<Uri, LibraryReader> libraries) {
_libraryReaders.addAll(libraries);
}
Namespace buildExportNamespace(
Uri uri,
List<ExportedReference> exportedReferences,
) {
var exportedNames = <String, Element>{};
for (var exportedReference in exportedReferences) {
var element = elementOfReference3(exportedReference.reference);
exportedNames[element.lookupName!] = element;
}
return Namespace(exportedNames);
}
LibraryElementImpl createLibraryElementForReading(Uri uri) {
var sourceFactory = analysisContext.sourceFactory;
var librarySource = sourceFactory.forUri2(uri)!;
var reader = _libraryReaders[uri];
if (reader == null) {
var rootChildren = rootReference.children.map((e) => e.name).toList();
if (rootChildren.length > 50) {
rootChildren = [
...rootChildren.take(50),
'... (${rootChildren.length} total)',
];
}
var readers = _libraryReaders.keys.map((uri) => uri.toString()).toList();
if (readers.length > 50) {
readers = [...readers.take(50), '... (${readers.length} total)'];
}
throw ArgumentError(
'Missing library: $uri\n'
'Libraries: $uriListWithLibraryElements\n'
'Root children: $rootChildren\n'
'Readers: $readers\n'
'Log: ${_logRing.join('\n')}\n',
);
}
var libraryElement = reader.readElement(librarySource: librarySource);
setLibraryTypeSystem(libraryElement);
return libraryElement;
}
void createTypeProviders(
LibraryElementImpl dartCore,
LibraryElementImpl dartAsync,
) {
if (analysisContext.hasTypeProvider) {
return;
}
analysisContext.setTypeProviders(
typeProvider: TypeProviderImpl(
coreLibrary: dartCore,
asyncLibrary: dartAsync,
),
);
// During linking we create libraries when typeProvider is not ready.
// Update these libraries now, when typeProvider is ready.
for (var reference in rootReference.children) {
var libraryElement = reference.element as LibraryElementImpl?;
if (libraryElement != null && !libraryElement.hasTypeProviderSystemSet) {
setLibraryTypeSystem(libraryElement);
}
}
}
void dispose() {
for (var libraryReference in rootReference.children) {
_disposeLibrary(libraryReference.element);
}
}
ElementImpl elementOfReference3(Reference reference) {
if (reference.element case var element?) {
return element;
}
if (reference.isLibrary) {
var uri = uriCache.parse(reference.name);
return createLibraryElementForReading(uri);
}
var parentRef = reference.parentNotContainer;
var parentElement = elementOfReference3(parentRef);
// Only classes delay creating children.
if (parentElement is ClassElementImpl) {
var firstFragment = parentElement.firstFragment;
// TODO(scheglov): directly ask to read all?
firstFragment.constructors;
parentElement.constructors;
}
var element = reference.element;
if (element == null) {
throw StateError('Expected existing element: $reference');
}
return element;
}
LibraryElementImpl? libraryOfUri(Uri uri) {
var reference = rootReference.getChild('$uri');
if (reference.element case LibraryElementImpl element) {
return element;
}
return createLibraryElementForReading(uri);
}
LibraryElementImpl libraryOfUri2(Uri uri) {
var element = libraryOfUri(uri);
if (element == null) {
libraryOfUri(uri);
throw StateError('No library: $uri');
}
return element;
}
/// We have linked the bundle, and need to disconnect its libraries, so
/// that the client can re-add the bundle, this time read from bytes.
void removeBundle(Set<Uri> uriSet) {
removeLibraries(uriSet);
}
/// Remove libraries with the specified URIs from the reference tree, and
/// any session level caches.
void removeLibraries(Set<Uri> uriSet) {
addToLogRing('[removeLibraries][uriSet: $uriSet][${StackTrace.current}]');
for (var uri in uriSet) {
_libraryReaders.remove(uri);
libraryManifests.remove(uri);
var libraryReference = rootReference.removeChild('$uri');
_disposeLibrary(libraryReference?.element);
}
analysisSession.classHierarchy.removeOfLibraries(uriSet);
analysisSession.inheritanceManager.removeOfLibraries(uriSet);
// If we discard `dart:core` and `dart:async`, we should also discard
// the type provider.
if (uriSet.contains(_dartCoreUri)) {
// Most of the time, if the `uriSet` contains `dart:core`, then it will
// also contain `dart:async`, since `dart:core` and `dart:async` are part
// of the same library cycle. However, if an event triggers `dart:core` to
// be discarded at a time when no library cycle information has been built
// yet, then just `dart:core` will be in `uriSet`. This can happen, for
// example, if two events trigger invalidation of `dart:core` in rapid
// succession. Fortunately, if this happens, it is benign; since no
// library cycle information has been built yet, there is nothing that
// that needs to be discarded.
if (_libraryReaders.isNotEmpty) {
throw StateError(
'Expected to link dart:core and dart:async first: '
'${_libraryReaders.keys.toList()}',
);
}
analysisContext.clearTypeProvider();
}
}
void replaceAnalysisSession(AnalysisSessionImpl newSession) {
analysisSession = newSession;
for (var libraryReference in rootReference.children) {
var libraryElement = libraryReference.element;
if (libraryElement is LibraryElementImpl) {
libraryElement.session = newSession;
}
}
}
void setLibraryTypeSystem(LibraryElementImpl libraryElement) {
// During linking we create libraries when typeProvider is not ready.
// And if we link dart:core and dart:async, we cannot create it.
// We will set typeProvider later, during [createTypeProviders].
if (!analysisContext.hasTypeProvider) {
return;
}
libraryElement.typeProvider = analysisContext.typeProvider;
libraryElement.typeSystem = analysisContext.typeSystem;
libraryElement.hasTypeProviderSystemSet = true;
}
void _disposeLibrary(ElementImpl? libraryElement) {}
}