| // 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 RootReference rootReference; | |
| final Map<Uri, LibraryReader> _libraryReaders = {}; | |
| bool isApplyingInformativeData = false; | |
| final Map<Uri, LibraryManifestHandle> 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); | |
| } | |
| BuiltInReference get dynamicRef { | |
| return rootReference.getOrCreateLibrary(_dartCoreUri).dynamicRef; | |
| } | |
| /// 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<ExportEntry> exportEntries) { | |
| var exportedNames = <String, Element>{}; | |
| for (var entry in exportEntries) { | |
| var element = elementOfReference3(entry.reference); | |
| exportedNames[entry.name] = element; | |
| } | |
| return Namespace(exportedNames); | |
| } | |
| 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 discardLibraryManifestInstances() { | |
| for (var handle in libraryManifests.values) { | |
| handle.discardInstance(); | |
| } | |
| } | |
| 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 case LibraryReference(:var uri)) { | |
| var result = _createLibraryElementForReading(uri); | |
| result ?? _reportMissingLibrary(uri); | |
| return result; | |
| } | |
| var enclosingRef = reference.enclosingReference; | |
| if (enclosingRef == null) { | |
| throw StateError('Expected an enclosing declaration: $reference'); | |
| } | |
| var enclosingElement = elementOfReference3(enclosingRef); | |
| // Read lazy children. | |
| if (enclosingElement is InstanceElementImpl) { | |
| enclosingElement.ensureReadMembers(); | |
| } | |
| var element = reference.element; | |
| if (element == null) { | |
| throw StateError('Expected existing element: $reference'); | |
| } | |
| return element; | |
| } | |
| LibraryManifestHandle? libraryManifestOfUri(Uri uri) { | |
| return libraryManifests[uri]; | |
| } | |
| LibraryElementImpl? libraryOfUri(Uri uri) { | |
| var reference = rootReference.getOrCreateLibrary(uri); | |
| if (reference.element case LibraryElementImpl element) { | |
| return element; | |
| } | |
| return _createLibraryElementForReading(uri); | |
| } | |
| LibraryElementImpl libraryOfUri2(Uri uri) { | |
| var element = libraryOfUri(uri); | |
| element ?? _reportMissingLibrary(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.removeLibrary(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; | |
| } | |
| LibraryElementImpl? _createLibraryElementForReading(Uri uri) { | |
| var sourceFactory = analysisContext.sourceFactory; | |
| var librarySource = sourceFactory.forUri2(uri); | |
| if (librarySource == null) { | |
| return null; | |
| } | |
| var reader = _libraryReaders[uri]; | |
| if (reader == null) { | |
| return null; | |
| } | |
| var libraryElement = reader.readElement(librarySource: librarySource); | |
| setLibraryTypeSystem(libraryElement); | |
| return libraryElement; | |
| } | |
| void _disposeLibrary(ElementImpl? libraryElement) {} | |
| Never _reportMissingLibrary(Uri uri) { | |
| var rootChildren = rootReference.children.map((e) => e.uriString).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', | |
| ); | |
| } | |
| } |