Add FileResolver.linkLibraries(), fix null typeProvider.
Change-Id: I4222097336407736e4cf0c3e922e9fac8c762725
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/167840
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Keerti Parthasarathy <keertip@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
index f28f239..1402523 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
@@ -235,14 +235,6 @@
librariesLoaded += cycle.libraries.length;
}
- // We are about to load dart:core, but if we have just linked it, the
- // linker might have set the type provider. So, clear it, and recreate
- // the element factory - it is empty anyway.
- if (!elementFactory.hasDartCore) {
- analysisContext.clearTypeProvider();
- _createElementFactory();
- }
-
var bundle = LinkedNodeBundle.fromBuffer(bytes);
inputBundles.add(bundle);
elementFactory.addBundle(
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index 53409d0..bd7ab2fa 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -271,6 +271,49 @@
return file.libraryCycle.signatureStr;
}
+ /// Ensure that libraries necessary for resolving [path] are linked.
+ ///
+ /// Libraries are linked in library cycles, from the bottom to top, so that
+ /// when we link a cycle, everything it transitively depends is ready. We
+ /// load newly linked libraries from bytes, and when we link a new library
+ /// cycle we partially resynthesize AST and elements from previously
+ /// loaded libraries.
+ ///
+ /// But when we are done linking libraries, and want to resolve just the
+ /// very top library that transitively depends on the whole dependency
+ /// tree, this library will not reference as many elements in the
+ /// dependencies as we needed for linking. Most probably it references
+ /// elements from directly imported libraries, and a couple of layers below.
+ /// So, keeping all previously resynthesized data is usually a waste.
+ ///
+ /// This method ensures that we discard the libraries context, with all its
+ /// partially resynthesized data, and so prepare for loading linked summaries
+ /// from bytes, which will be done by [getErrors]. It is OK for it to
+ /// spend some more time on this.
+ void linkLibraries({
+ @required String path,
+ }) {
+ _throwIfNotAbsoluteNormalizedPath(path);
+
+ var performance = OperationPerformanceImpl('<unused>');
+
+ _withLibraryContextReset(() {
+ var fileContext = getFileContext(
+ path: path,
+ performance: performance,
+ );
+ var file = fileContext.file;
+ var libraryFile = file.partOfLibrary ?? file;
+
+ libraryContext.load2(
+ targetLibrary: libraryFile,
+ performance: performance,
+ );
+ });
+
+ _resetContextObjects();
+ }
+
/// The [completionLine] and [completionColumn] are zero based.
ResolvedUnitResult resolve({
int completionLine,
@@ -531,6 +574,13 @@
}
}
+ void _resetContextObjects() {
+ if (libraryContext != null) {
+ contextObjects = null;
+ libraryContext = null;
+ }
+ }
+
void _throwIfNotAbsoluteNormalizedPath(String path) {
var pathContext = resourceProvider.pathContext;
if (pathContext.normalize(path) != path) {
@@ -691,14 +741,6 @@
performance.getDataInt('libraryLoadCount').add(cycle.libraries.length);
}
- // We are about to load dart:core, but if we have just linked it, the
- // linker might have set the type provider. So, clear it, and recreate
- // the element factory - it is empty anyway.
- if (!elementFactory.hasDartCore) {
- contextObjects.analysisContext.clearTypeProvider();
- elementFactory.declareDartCoreDynamicNever();
- }
-
var cBundle = CiderLinkedLibraryCycle.fromBuffer(bytes);
inputBundles.add(cBundle.bundle);
elementFactory.addBundle(
@@ -715,6 +757,9 @@
);
}
}
+
+ // We might have just linked dart:core, ensure the type provider.
+ _createElementFactoryTypeProvider();
}
logger.run('Prepare linked bundles', () {
@@ -732,7 +777,7 @@
/// If we need these libraries later, we will relink and reattach them.
void remove(List<FileState> removed) {
elementFactory.removeLibraries(
- removed.map((e) => e.uriStr).toList(),
+ removed.map((e) => e.uriStr).toSet(),
);
var removedSet = removed.toSet();
@@ -741,6 +786,16 @@
});
}
+ /// Ensure that type provider is created.
+ void _createElementFactoryTypeProvider() {
+ var analysisContext = contextObjects.analysisContext;
+ if (analysisContext.typeProviderNonNullableByDefault == null) {
+ var dartCore = elementFactory.libraryOfUri('dart:core');
+ var dartAsync = elementFactory.libraryOfUri('dart:async');
+ elementFactory.createTypeProviders(dartCore, dartAsync);
+ }
+ }
+
static CiderLinkedLibraryCycleBuilder serializeBundle(
List<int> signature, link2.LinkResult linkResult) {
return CiderLinkedLibraryCycleBuilder(
@@ -791,10 +846,7 @@
if (resetTimeout != null) {
_timer = Timer(resetTimeout, () {
_timer = null;
- if (fileResolver.libraryContext != null) {
- fileResolver.contextObjects = null;
- fileResolver.libraryContext = null;
- }
+ fileResolver._resetContextObjects();
});
}
}
diff --git a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
index 8dd1b7c..abfaeec 100644
--- a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
@@ -28,7 +28,7 @@
) {
ArgumentError.checkNotNull(analysisContext, 'analysisContext');
ArgumentError.checkNotNull(analysisSession, 'analysisSession');
- declareDartCoreDynamicNever();
+ _declareDartCoreDynamicNever();
}
Reference get dynamicRef {
@@ -95,12 +95,6 @@
}
}
- void declareDartCoreDynamicNever() {
- var dartCoreRef = rootReference.getChild('dart:core');
- dartCoreRef.getChild('dynamic').element = DynamicElementImpl.instance;
- dartCoreRef.getChild('Never').element = NeverElementImpl.instance;
- }
-
Element elementOfReference(Reference reference) {
if (reference.element != null) {
return reference.element;
@@ -147,19 +141,38 @@
/// 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(LinkedBundleContext context) {
- var uriStrList = context.libraryMap.keys.toList();
- removeLibraries(uriStrList);
+ var uriStrSet = context.libraryMap.keys.toSet();
+ removeLibraries(uriStrSet);
+
+ // This is the bundle with dart:core and dart:async, based on full ASTs.
+ // To link them, the linker set the type provider. We are removing these
+ // libraries, and we should also remove the type provider.
+ if (uriStrSet.contains('dart:core')) {
+ if (!uriStrSet.contains('dart:async')) {
+ throw StateError(
+ 'Expected to link dart:core and dart:async together: '
+ '${uriStrSet.toList()}',
+ );
+ }
+ if (libraryMap.isNotEmpty) {
+ throw StateError(
+ 'Expected to link dart:core and dart:async first: '
+ '${libraryMap.keys.toList()}',
+ );
+ }
+ analysisContext.clearTypeProvider();
+ _declareDartCoreDynamicNever();
+ }
}
/// Remove libraries with the specified URIs from the reference tree, and
/// any session level caches.
- void removeLibraries(List<String> uriStrList) {
- for (var uriStr in uriStrList) {
+ void removeLibraries(Set<String> uriStrSet) {
+ for (var uriStr in uriStrSet) {
libraryMap.remove(uriStr);
rootReference.removeChild(uriStr);
}
- var uriStrSet = uriStrList.toSet();
analysisSession.classHierarchy.removeOfLibraries(uriStrSet);
analysisSession.inheritanceManager.removeOfLibraries(uriStrSet);
}
@@ -181,6 +194,12 @@
}
}
+ void _declareDartCoreDynamicNever() {
+ var dartCoreRef = rootReference.getChild('dart:core');
+ dartCoreRef.getChild('dynamic').element = DynamicElementImpl.instance;
+ dartCoreRef.getChild('Never').element = NeverElementImpl.instance;
+ }
+
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.
diff --git a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
index 45500b8..5a7a974 100644
--- a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
+++ b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
@@ -464,6 +464,24 @@
]);
}
+ test_linkLibraries_getErrors() {
+ addTestFile(r'''
+var a = b;
+var foo = 0;
+''');
+
+ var path = convertPath('/workspace/dart/test/lib/test.dart');
+ fileResolver.linkLibraries(path: path);
+
+ var result = getTestErrors();
+ expect(result.path, path);
+ expect(result.uri.toString(), 'package:dart.test/test.dart');
+ assertErrorsInList(result.errors, [
+ error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 8, 1),
+ ]);
+ expect(result.lineInfo.lineStarts, [0, 11, 24]);
+ }
+
test_nullSafety_enabled() async {
typeToStringWithNullability = true;