diff --git a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart index 4db5fcff946..be5b2ba5ae1 100644 --- a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart +++ b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart @@ -110,7 +110,7 @@ class LibraryAnalyzer { // Parse all files. performance.run('parse', (performance) { - for (FileState file in _library.libraryFiles) { + for (FileState file in _library.files().ofLibrary) { if (completionPath == null || file.path == completionPath) { units[file] = _parse( file: file, @@ -231,19 +231,19 @@ class LibraryAnalyzer { if (_analysisOptions.lint) { performance.run('computeLints', (performance) { - var allUnits = _library.libraryFiles.map((file) { + var allUnits = _library.files().ofLibrary.map((file) { var content = getFileContent(file); return LinterContextUnit(content, units[file]!); }).toList(); for (int i = 0; i < allUnits.length; i++) { - _computeLints(_library.libraryFiles[i], allUnits[i], allUnits); + _computeLints(_library.files().ofLibrary[i], allUnits[i], allUnits); } }); } // This must happen after all other diagnostics have been computed but // before the list of diagnostics has been filtered. - for (var file in _library.libraryFiles) { + for (var file in _library.files().ofLibrary) { IgnoreValidator( _getErrorReporter(file), _getErrorListener(file).errors, @@ -472,7 +472,7 @@ class LibraryAnalyzer { } bool _isExistingSource(Source source) { - for (var file in _library.directReferencedFiles) { + for (var file in _library.files().directReferencedFiles) { if (file.uri == source.uri) { return file.exists; } @@ -595,7 +595,7 @@ class LibraryAnalyzer { } else if (directive is PartDirectiveImpl) { StringLiteral partUri = directive.uri; - FileState partFile = _library.partedFiles[partIndex]; + FileState partFile = _library.files().parted[partIndex]; var partUnit = units[partFile]!; CompilationUnitElement partElement = _libraryElement.parts[partIndex]; partUnit.element = partElement; diff --git a/pkg/analyzer/lib/src/dart/micro/library_graph.dart b/pkg/analyzer/lib/src/dart/micro/library_graph.dart index 2626139024b..a0379af02b1 100644 --- a/pkg/analyzer/lib/src/dart/micro/library_graph.dart +++ b/pkg/analyzer/lib/src/dart/micro/library_graph.dart @@ -112,69 +112,22 @@ class CiderUnlinkedUnit { } class FileState { - final FileSystemState _fsState; - - /// The path of the file. - final String path; - - /// The URI of the file. - final Uri uri; - - /// The [Source] of the file with the [uri]. - final Source source; - - /// The [WorkspacePackage] that contains this file. - /// - /// It might be `null` if the file is outside of the workspace. - final WorkspacePackage? workspacePackage; - - /// The [FeatureSet] for all files in the analysis context. - /// - /// Usually it is the feature set of the latest language version, plus - /// possibly additional enabled experiments (from the analysis options file, - /// or from SDK allowed experiments). - /// - /// This feature set is then restricted, with the [_packageLanguageVersion], - /// or with a `@dart` language override token in the file header. - final FeatureSet _contextFeatureSet; - - /// The language version for the package that contains this file. - final Version _packageLanguageVersion; + final _FileStateUnlinked _unlinked; /// Files that reference this file. final List referencingFiles = []; - final List importedFiles = []; - final List exportedFiles = []; - final List partedFiles = []; - final Set directReferencedFiles = {}; - final Set directReferencedLibraries = {}; - final List libraryFiles = []; - FileState? partOfLibrary; + _FileStateFiles? _files; - late Uint8List _digest; - late bool _exists; - late CiderUnlinkedUnit unlinked; LibraryCycle? _libraryCycle; - /// id of the cache entry with unlinked data. - late int unlinkedId; + FileState._(this._unlinked); - FileState._( - this._fsState, - this.path, - this.uri, - this.source, - this.workspacePackage, - this._contextFeatureSet, - this._packageLanguageVersion, - ); + Uint8List get apiSignature => unlinkedUnit.apiSignature; - Uint8List get apiSignature => unlinked.unit.apiSignature; + Uint8List get digest => _unlinked.digest; - Uint8List get digest => _digest; - - bool get exists => _exists; + bool get exists => _unlinked.exists; /// Return the [LibraryCycle] this file belongs to, even if it consists of /// just this file. If the library cycle is not known yet, compute it. @@ -185,7 +138,11 @@ class FileState { return _libraryCycle!; } - LineInfo get lineInfo => LineInfo(unlinked.unit.lineStarts); + LineInfo get lineInfo => LineInfo(unlinkedUnit.lineStarts); + + FileState? get partOfLibrary => _unlinked.partOfLibrary; + + String get path => _location.path; /// The resolved signature of the file, that depends on the [libraryCycle] /// signature, and the content of the file. @@ -194,47 +151,62 @@ class FileState { signatureBuilder.addString(path); signatureBuilder.addBytes(libraryCycle.signature); - var content = getContentWithSameDigest(); + var content = getContent(); signatureBuilder.addString(content); return signatureBuilder.toHex(); } + Source get source => _location.source; + + int get unlinkedId => _unlinked.unlinkedId; + + UnlinkedUnit get unlinkedUnit => _unlinked.unlinked.unit; + + Uri get uri => _location.uri; + /// Return the [uri] string. String get uriStr => uri.toString(); + WorkspacePackage? get workspacePackage => _location.workspacePackage; + + FileSystemState get _fsState => _location._fsState; + + _FileStateLocation get _location => _unlinked.location; + /// Collect all files that are transitively referenced by this file via /// imports, exports, and parts. void collectAllReferencedFiles(Set referencedFiles) { - for (var file in {...importedFiles, ...exportedFiles, ...partedFiles}) { + for (var file in files().directReferencedFiles) { if (referencedFiles.add(file.path)) { file.collectAllReferencedFiles(referencedFiles); } } } - /// Return the content of the file, the empty string if cannot be read. - String getContent() { - try { - var resource = _fsState._resourceProvider.getFile(path); - return resource.readAsStringSync(); - } catch (_) { - return ''; - } + _FileStateFiles files({ + OperationPerformanceImpl? performance, + }) { + return _files ??= _FileStateFiles( + owner: this, + performance: performance ?? OperationPerformanceImpl(''), + ); } /// Return the content of the file, the empty string if cannot be read. /// - /// Additionally, we read the file digest, end verify that it is the same - /// as the [_digest] that we recorded in [refresh]. If it is not, then the - /// file was changed, and we failed to call [FileSystemState.changeFile] - String getContentWithSameDigest() { - var digest = utf8.encode(_fsState.getFileDigest(path)); - if (!const ListEquality().equals(digest, _digest)) { + /// We read the file digest, end verify that it is the same as the digest + /// that was recorded during the file creation. If it is not, then the file + /// was changed, and we failed to call [FileSystemState.changeFile]. + String getContent() { + var contentWithDigest = _location._getContent(); + + var digest = contentWithDigest.digest; + if (!const ListEquality().equals(digest, _unlinked.digest)) { throw StateError('File was changed, but not invalidated: $path'); } - return getContent(); + return contentWithDigest.content; } void internal_setLibraryCycle(LibraryCycle cycle, String signature) { @@ -243,366 +215,13 @@ class FileState { CompilationUnitImpl parse( AnalysisErrorListener errorListener, String content) { - CharSequenceReader reader = CharSequenceReader(content); - Scanner scanner = Scanner(source, reader, errorListener) - ..configureFeatures( - featureSetForOverriding: _contextFeatureSet, - featureSet: _contextFeatureSet.restrictToVersion( - _packageLanguageVersion, - ), - ); - Token token = scanner.tokenize(reportScannerErrors: false); - LineInfo lineInfo = LineInfo(scanner.lineStarts); - - // Pass the feature set from the scanner to the parser - // because the scanner may have detected a language version comment - // and downgraded the feature set it holds. - Parser parser = Parser( - source, - errorListener, - featureSet: scanner.featureSet, - ); - parser.enableOptionalNewAndConst = true; - var unit = parser.parseCompilationUnit(token); - unit.lineInfo = lineInfo; - - // StringToken uses a static instance of StringCanonicalizer, so we need - // to clear it explicitly once we are done using it for this file. - StringToken.canonicalizer.clear(); - - // TODO(scheglov) Use actual versions. - unit.languageVersion = LibraryLanguageVersion( - package: ExperimentStatus.currentVersion, - override: null, - ); - - return unit; - } - - void refresh({ - FileState? containingLibrary, - required OperationPerformanceImpl performance, - }) { - _fsState.testView.refreshedFiles.add(path); - performance.getDataInt('count').increment(); - - performance.run('digest', (_) { - _digest = utf8.encode(_fsState.getFileDigest(path)) as Uint8List; - _exists = _digest.isNotEmpty; - }); - - String unlinkedKey = '$path.unlinked'; - - // Prepare bytes of the unlinked bundle - existing or new. - // TODO(migration): should not be nullable - Uint8List? unlinkedBytes; - { - var unlinkedData = _fsState._byteStore.get(unlinkedKey, _digest); - unlinkedBytes = unlinkedData?.bytes; - - if (unlinkedBytes == null || unlinkedBytes.isEmpty) { - var content = performance.run('content', (_) { - return getContent(); - }); - - var unit = performance.run('parse', (performance) { - performance.getDataInt('count').increment(); - performance.getDataInt('length').add(content.length); - return parse(AnalysisErrorListener.NULL_LISTENER, content); - }); - - performance.run('unlinked', (performance) { - var unlinkedBuilder = serializeAstCiderUnlinked(unit); - unlinkedBytes = unlinkedBuilder.toBytes(); - performance.getDataInt('length').add(unlinkedBytes!.length); - unlinkedData = - _fsState._byteStore.putGet(unlinkedKey, _digest, unlinkedBytes!); - unlinkedBytes = unlinkedData!.bytes; - }); - - unlinked = CiderUnlinkedUnit.fromBytes(unlinkedBytes!); - - // TODO(scheglov) We decode above only because we call it here. - performance.run('prefetch', (_) { - _prefetchDirectReferences(unlinked.unit); - }); - } - unlinkedId = unlinkedData!.id; - } - - // Read the unlinked bundle. - unlinked = CiderUnlinkedUnit.fromBytes(unlinkedBytes!); - - // Build the graph. - for (var directive in unlinked.unit.imports) { - var file = _fileForRelativeUri( - relativeUri: directive.uri, - performance: performance, - ); - if (file != null) { - importedFiles.add(file); - } - } - for (var directive in unlinked.unit.exports) { - var file = _fileForRelativeUri( - relativeUri: directive.uri, - performance: performance, - ); - if (file != null) { - exportedFiles.add(file); - } - } - for (var uri in unlinked.unit.parts) { - var file = _fileForRelativeUri( - containingLibrary: this, - relativeUri: uri, - performance: performance, - ); - if (file != null) { - partedFiles.add(file); - } - } - if (unlinked.unit.hasPartOfDirective) { - if (containingLibrary == null) { - _fsState.testView.partsDiscoveredLibraries.add(path); - var libraryName = unlinked.unit.partOfName; - var libraryUri = unlinked.unit.partOfUri; - partOfLibrary = null; - if (libraryName != null) { - _findPartOfNameLibrary(performance: performance); - } else if (libraryUri != null) { - partOfLibrary = _fileForRelativeUri( - relativeUri: libraryUri, - performance: performance, - ); - } - } else { - partOfLibrary = containingLibrary; - } - if (partOfLibrary != null) { - directReferencedFiles.add(partOfLibrary!); - } - } - libraryFiles.add(this); - libraryFiles.addAll(partedFiles); - - // Compute referenced files. - directReferencedFiles - ..addAll(importedFiles) - ..addAll(exportedFiles) - ..addAll(partedFiles); - directReferencedLibraries - ..addAll(importedFiles) - ..addAll(exportedFiles); + return _FileStateUnlinked.parse(errorListener, _location, content); } @override String toString() { return path; } - - FileState? _fileForRelativeUri({ - FileState? containingLibrary, - required String relativeUri, - required OperationPerformanceImpl performance, - }) { - if (relativeUri.isEmpty) { - return null; - } - - Uri absoluteUri; - try { - absoluteUri = resolveRelativeUri(uri, Uri.parse(relativeUri)); - } on FormatException { - return null; - } - - var file = _fsState.getFileForUri( - containingLibrary: containingLibrary, - uri: absoluteUri, - performance: performance, - ); - if (file == null) { - return null; - } - - file.referencingFiles.add(this); - return file; - } - - /// This file has a `part of some.library;` directive. Because it does not - /// specify the URI of the library, we don't know the library for sure. - /// But usually the library is one of the sibling files. - void _findPartOfNameLibrary({ - required OperationPerformanceImpl performance, - }) { - var resourceProvider = _fsState._resourceProvider; - var pathContext = resourceProvider.pathContext; - - var children = []; - try { - var parent = resourceProvider.getFile(path).parent2; - children = parent.getChildren(); - } catch (_) {} - - for (var siblingFile in children) { - if (file_paths.isDart(pathContext, siblingFile.path)) { - var childState = _fsState.getFileForPath( - path: siblingFile.path, - performance: performance, - ); - if (childState.partedFiles.contains(this)) { - partOfLibrary = childState; - break; - } - } - } - } - - void _prefetchDirectReferences(UnlinkedUnit unlinkedUnit2) { - if (_fsState.prefetchFiles == null) { - return; - } - - var paths = {}; - - void findPathForUri(String relativeUri) { - if (relativeUri.isEmpty) { - return; - } - Uri absoluteUri; - try { - absoluteUri = resolveRelativeUri(uri, Uri.parse(relativeUri)); - } on FormatException { - return; - } - var p = _fsState.getPathForUri(absoluteUri); - if (p != null) { - paths.add(p); - } - } - - for (var directive in unlinked.unit.imports) { - findPathForUri(directive.uri); - } - for (var directive in unlinked.unit.exports) { - findPathForUri(directive.uri); - } - for (var uri in unlinked.unit.parts) { - findPathForUri(uri); - } - _fsState.prefetchFiles!(paths.toList()); - } - - static CiderUnlinkedUnit serializeAstCiderUnlinked(CompilationUnit unit) { - var exports = []; - var imports = []; - var parts = []; - var hasDartCoreImport = false; - var hasLibraryDirective = false; - var hasPartOfDirective = false; - String? partOfName; - String? partOfUriStr; - for (var directive in unit.directives) { - if (directive is ExportDirective) { - var builder = _serializeNamespaceDirective(directive); - exports.add(builder); - } else if (directive is ImportDirective) { - var builder = _serializeNamespaceDirective(directive); - imports.add(builder); - if (builder.uri == 'dart:core') { - hasDartCoreImport = true; - } - } else if (directive is LibraryDirective) { - hasLibraryDirective = true; - } else if (directive is PartDirective) { - var uriStr = directive.uri.stringValue; - parts.add(uriStr ?? ''); - } else if (directive is PartOfDirective) { - hasPartOfDirective = true; - var libraryName = directive.libraryName; - var uriStr = directive.uri?.stringValue; - if (libraryName != null) { - partOfName = libraryName.components.map((e) => e.name).join('.'); - } else if (uriStr != null) { - partOfUriStr = uriStr; - } - } - } - if (!hasDartCoreImport) { - imports.add( - UnlinkedNamespaceDirective( - configurations: [], - uri: 'dart:core', - ), - ); - } - - var declaredExtensions = []; - var declaredFunctions = []; - var declaredTypes = []; - var declaredVariables = []; - for (var declaration in unit.declarations) { - if (declaration is ClassDeclaration) { - declaredTypes.add(declaration.name.name); - } else if (declaration is EnumDeclaration) { - declaredTypes.add(declaration.name.name); - } else if (declaration is ExtensionDeclaration) { - var name = declaration.name; - if (name != null) { - declaredExtensions.add(name.name); - } - } else if (declaration is FunctionDeclaration) { - declaredFunctions.add(declaration.name.name); - } else if (declaration is MixinDeclaration) { - declaredTypes.add(declaration.name.name); - } else if (declaration is TopLevelVariableDeclaration) { - for (var variable in declaration.variables.variables) { - declaredVariables.add(variable.name.name); - } - } - } - - var unlinkedUnit = UnlinkedUnit( - apiSignature: computeUnlinkedApiSignature(unit), - exports: exports, - hasLibraryDirective: hasLibraryDirective, - hasPartOfDirective: hasPartOfDirective, - imports: imports, - informativeBytes: writeUnitInformative(unit), - lineStarts: Uint32List.fromList(unit.lineInfo!.lineStarts), - partOfName: partOfName, - partOfUri: partOfUriStr, - parts: parts, - ); - - return CiderUnlinkedUnit( - unit: unlinkedUnit, - topLevelDeclarations: CiderUnitTopLevelDeclarations( - extensionNames: declaredExtensions, - functionNames: declaredFunctions, - typeNames: declaredTypes, - variableNames: declaredVariables, - ), - ); - } - - static UnlinkedNamespaceDirective _serializeNamespaceDirective( - NamespaceDirective directive, - ) { - return UnlinkedNamespaceDirective( - configurations: directive.configurations.map((configuration) { - var name = configuration.name.components.join('.'); - var value = configuration.value?.stringValue ?? ''; - return UnlinkedNamespaceDirectiveConfiguration( - name: name, - value: value, - uri: configuration.uri.stringValue ?? '', - ); - }).toList(), - uri: directive.uri.stringValue ?? '', - ); - } } class FileSystemState { @@ -659,7 +278,7 @@ class FileSystemState { _uriToFile.remove(file.uri); // The removed file does not reference other file anymore. - for (var referencedFile in file.directReferencedFiles) { + for (var referencedFile in file.files().directReferencedFiles) { referencedFile.referencingFiles.remove(file); } @@ -674,7 +293,7 @@ class FileSystemState { Set collectSharedDataIdentifiers() { var result = {}; for (var file in _pathToFile.values) { - result.add(file.unlinkedId); + result.add(file._unlinked.unlinkedId); } return result; } @@ -730,17 +349,21 @@ class FileSystemState { var featureSet = contextFeatureSet(path, uri, workspacePackage); var packageLanguageVersion = contextLanguageVersion(path, uri, workspacePackage); - file = FileState._(this, path, uri, source, workspacePackage, featureSet, - packageLanguageVersion); + var location = _FileStateLocation._(this, path, uri, source, + workspacePackage, featureSet, packageLanguageVersion); + file = FileState._( + _FileStateUnlinked( + location: location, + partOfLibrary: null, + performance: performance, + ), + ); _pathToFile[path] = file; _uriToFile[uri] = file; - performance.run('refresh', (performance) { - file!.refresh( - performance: performance, - ); - }); + // Recurse with recording performance. + file.files(performance: performance); } return file; } @@ -763,15 +386,21 @@ class FileSystemState { var packageLanguageVersion = contextLanguageVersion(path, uri, workspacePackage); - file = FileState._(this, path, uri, source, workspacePackage, featureSet, - packageLanguageVersion); + var location = _FileStateLocation._(this, path, uri, source, + workspacePackage, featureSet, packageLanguageVersion); + file = FileState._( + _FileStateUnlinked( + location: location, + partOfLibrary: containingLibrary, + performance: performance, + ), + ); + _pathToFile[path] = file; _uriToFile[uri] = file; - file.refresh( - containingLibrary: containingLibrary, - performance: performance, - ); + // Recurse with recording performance. + file.files(performance: performance); } return file; } @@ -807,7 +436,7 @@ class FileSystemState { } } - var topLevelDeclarations = file.unlinked.topLevelDeclarations; + var topLevelDeclarations = file._unlinked.unlinked.topLevelDeclarations; addDeclaration( topLevelDeclarations.extensionNames, FileTopLevelDeclarationKind.extension, @@ -954,6 +583,16 @@ class LibraryCycle { } } +class _ContentWithDigest { + final String content; + final Uint8List digest; + + _ContentWithDigest({ + required this.content, + required this.digest, + }); +} + class _FakeSource implements Source { @override final String fullName; @@ -967,6 +606,486 @@ class _FakeSource implements Source { dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } +class _FileStateFiles { + final List imported = []; + final List exported = []; + final List parted = []; + final List ofLibrary = []; + + _FileStateFiles({ + required FileState owner, + required OperationPerformanceImpl performance, + }) { + var unlinked = owner._unlinked; + var location = unlinked.location; + var unlinkedUnit = unlinked.unlinked.unit; + + // Build the graph. + for (var directive in unlinkedUnit.imports) { + var file = location._fileForRelativeUri( + relativeUri: directive.uri, + performance: performance, + ); + if (file != null) { + file.referencingFiles.add(owner); + imported.add(file); + } + } + for (var directive in unlinkedUnit.exports) { + var file = location._fileForRelativeUri( + relativeUri: directive.uri, + performance: performance, + ); + if (file != null) { + exported.add(file); + file.referencingFiles.add(owner); + } + } + for (var uri in unlinkedUnit.parts) { + var file = location._fileForRelativeUri( + containingLibrary: owner, + relativeUri: uri, + performance: performance, + ); + if (file != null) { + parted.add(file); + file.referencingFiles.add(owner); + } + } + + ofLibrary.add(owner); + ofLibrary.addAll(parted); + } + + /// Return all directly referenced files - imported, exported or parted. + Set get directReferencedFiles { + return {...imported, ...exported, ...parted}; + } + + /// Return all directly referenced libraries - imported or exported. + Set get directReferencedLibraries { + return {...imported, ...exported}; + } +} + +class _FileStateLocation { + final FileSystemState _fsState; + + /// The path of the file. + final String path; + + /// The URI of the file. + final Uri uri; + + /// The [Source] of the file with the [uri]. + final Source source; + + /// The [WorkspacePackage] that contains this file. + /// + /// It might be `null` if the file is outside of the workspace. + final WorkspacePackage? workspacePackage; + + /// The [FeatureSet] for all files in the analysis context. + /// + /// Usually it is the feature set of the latest language version, plus + /// possibly additional enabled experiments (from the analysis options file, + /// or from SDK allowed experiments). + /// + /// This feature set is then restricted, with the [_packageLanguageVersion], + /// or with a `@dart` language override token in the file header. + final FeatureSet _contextFeatureSet; + + /// The language version for the package that contains this file. + final Version _packageLanguageVersion; + + _FileStateLocation._( + this._fsState, + this.path, + this.uri, + this.source, + this.workspacePackage, + this._contextFeatureSet, + this._packageLanguageVersion, + ); + + File get resource { + return _fsState._resourceProvider.getFile(path); + } + + FileState? _fileForRelativeUri({ + FileState? containingLibrary, + required String relativeUri, + required OperationPerformanceImpl performance, + }) { + if (relativeUri.isEmpty) { + return null; + } + + Uri absoluteUri; + try { + absoluteUri = resolveRelativeUri(uri, Uri.parse(relativeUri)); + } on FormatException { + return null; + } + + return _fsState.getFileForUri( + containingLibrary: containingLibrary, + uri: absoluteUri, + performance: performance, + ); + } + + /// This file has a `part of some.library;` directive. Because it does not + /// specify the URI of the library, we don't know the library for sure. + /// But usually the library is one of the sibling files. + FileState? _findPartOfNameLibrary({ + required OperationPerformanceImpl performance, + }) { + var resourceProvider = _fsState._resourceProvider; + var pathContext = resourceProvider.pathContext; + + var siblings = []; + try { + siblings = resource.parent2.getChildren(); + } catch (_) {} + + for (var sibling in siblings) { + if (file_paths.isDart(pathContext, sibling.path)) { + var siblingState = _fsState.getFileForPath( + path: sibling.path, + performance: performance, + ); + if (siblingState.files().parted.any((part) => part.path == path)) { + return siblingState; + } + } + } + } + + _ContentWithDigest _getContent() { + String content; + try { + content = resource.readAsStringSync(); + } catch (_) { + content = ''; + } + + var digestStr = _fsState.getFileDigest(path); + var digest = utf8.encode(digestStr) as Uint8List; + + return _ContentWithDigest(content: content, digest: digest); + } +} + +class _FileStateUnlinked { + final _FileStateLocation location; + FileState? _partOfLibrary; + + final Uint8List digest; + final bool exists; + final CiderUnlinkedUnit unlinked; + + /// id of the cache entry with unlinked data. + final int unlinkedId; + + factory _FileStateUnlinked({ + required _FileStateLocation location, + required FileState? partOfLibrary, + required OperationPerformanceImpl performance, + }) { + location._fsState.testView.refreshedFiles.add(location.path); + + int unlinkedId; + CiderUnlinkedUnit unlinked; + + var digest = performance.run('digest', (performance) { + performance.getDataInt('count').increment(); + var digestStr = location._fsState.getFileDigest(location.path); + return utf8.encode(digestStr) as Uint8List; + }); + + var exists = digest.isNotEmpty; + + var unlinkedKey = '${location.path}.unlinked'; + var isUnlinkedFromCache = true; + + // Prepare bytes of the unlinked bundle - existing or new. + // TODO(migration): should not be nullable + Uint8List? unlinkedBytes; + { + var unlinkedData = location._fsState._byteStore.get(unlinkedKey, digest); + unlinkedBytes = unlinkedData?.bytes; + + if (unlinkedBytes == null || unlinkedBytes.isEmpty) { + isUnlinkedFromCache = false; + + var contentWithDigest = performance.run('content', (_) { + return location._getContent(); + }); + digest = contentWithDigest.digest; + var content = contentWithDigest.content; + + var unit = performance.run('parse', (performance) { + performance.getDataInt('count').increment(); + performance.getDataInt('length').add(content.length); + return parse(AnalysisErrorListener.NULL_LISTENER, location, content); + }); + + performance.run('unlinked', (performance) { + var unlinkedUnit = serializeAstCiderUnlinked(unit); + unlinkedBytes = unlinkedUnit.toBytes(); + performance.getDataInt('length').add(unlinkedBytes!.length); + unlinkedData = location._fsState._byteStore + .putGet(unlinkedKey, digest, unlinkedBytes!); + unlinkedBytes = unlinkedData!.bytes; + }); + + unlinked = CiderUnlinkedUnit.fromBytes(unlinkedBytes!); + } + unlinkedId = unlinkedData!.id; + } + + // Read the unlinked bundle. + unlinked = CiderUnlinkedUnit.fromBytes(unlinkedBytes!); + + var result = _FileStateUnlinked._( + location: location, + partOfLibrary: partOfLibrary, + digest: digest, + exists: exists, + unlinked: unlinked, + unlinkedId: unlinkedId, + ); + if (isUnlinkedFromCache) { + performance.run('prefetch', (_) { + result._prefetchDirectReferences(); + }); + } + return result; + } + + _FileStateUnlinked._({ + required this.location, + required FileState? partOfLibrary, + required this.digest, + required this.exists, + required this.unlinked, + required this.unlinkedId, + }) : _partOfLibrary = partOfLibrary; + + FileState? get partOfLibrary { + var partOfLibrary = _partOfLibrary; + if (partOfLibrary != null) { + return partOfLibrary; + } + + var performance = OperationPerformanceImpl(''); + + var libraryName = unlinked.unit.partOfName; + if (libraryName != null) { + location._fsState.testView.partsDiscoveredLibraries.add(location.path); + return _partOfLibrary = location._findPartOfNameLibrary( + performance: performance, + ); + } + + var libraryUri = unlinked.unit.partOfUri; + if (libraryUri != null) { + location._fsState.testView.partsDiscoveredLibraries.add(location.path); + return _partOfLibrary = location._fileForRelativeUri( + relativeUri: libraryUri, + performance: performance, + ); + } + } + + void _prefetchDirectReferences() { + if (location._fsState.prefetchFiles == null) { + return; + } + + var paths = {}; + + /// TODO(scheglov) This is duplicate. + void findPathForUri(String relativeUri) { + if (relativeUri.isEmpty) { + return; + } + Uri absoluteUri; + try { + absoluteUri = resolveRelativeUri(location.uri, Uri.parse(relativeUri)); + } on FormatException { + return; + } + var p = location._fsState.getPathForUri(absoluteUri); + if (p != null) { + paths.add(p); + } + } + + var unlinkedUnit = unlinked.unit; + for (var directive in unlinkedUnit.imports) { + findPathForUri(directive.uri); + } + for (var directive in unlinkedUnit.exports) { + findPathForUri(directive.uri); + } + for (var uri in unlinkedUnit.parts) { + findPathForUri(uri); + } + + location._fsState.prefetchFiles!(paths.toList()); + } + + static CompilationUnitImpl parse(AnalysisErrorListener errorListener, + _FileStateLocation location, String content) { + CharSequenceReader reader = CharSequenceReader(content); + Scanner scanner = Scanner(location.source, reader, errorListener) + ..configureFeatures( + featureSetForOverriding: location._contextFeatureSet, + featureSet: location._contextFeatureSet.restrictToVersion( + location._packageLanguageVersion, + ), + ); + Token token = scanner.tokenize(reportScannerErrors: false); + LineInfo lineInfo = LineInfo(scanner.lineStarts); + + // Pass the feature set from the scanner to the parser + // because the scanner may have detected a language version comment + // and downgraded the feature set it holds. + Parser parser = Parser( + location.source, + errorListener, + featureSet: scanner.featureSet, + ); + parser.enableOptionalNewAndConst = true; + var unit = parser.parseCompilationUnit(token); + unit.lineInfo = lineInfo; + + // StringToken uses a static instance of StringCanonicalizer, so we need + // to clear it explicitly once we are done using it for this file. + StringToken.canonicalizer.clear(); + + // TODO(scheglov) Use actual versions. + unit.languageVersion = LibraryLanguageVersion( + package: ExperimentStatus.currentVersion, + override: null, + ); + + return unit; + } + + static CiderUnlinkedUnit serializeAstCiderUnlinked(CompilationUnit unit) { + var exports = []; + var imports = []; + var parts = []; + var hasDartCoreImport = false; + var hasLibraryDirective = false; + var hasPartOfDirective = false; + String? partOfName; + String? partOfUriStr; + for (var directive in unit.directives) { + if (directive is ExportDirective) { + var builder = _serializeNamespaceDirective(directive); + exports.add(builder); + } else if (directive is ImportDirective) { + var builder = _serializeNamespaceDirective(directive); + imports.add(builder); + if (builder.uri == 'dart:core') { + hasDartCoreImport = true; + } + } else if (directive is LibraryDirective) { + hasLibraryDirective = true; + } else if (directive is PartDirective) { + var uriStr = directive.uri.stringValue; + parts.add(uriStr ?? ''); + } else if (directive is PartOfDirective) { + hasPartOfDirective = true; + var libraryName = directive.libraryName; + var uriStr = directive.uri?.stringValue; + if (libraryName != null) { + partOfName = libraryName.components.map((e) => e.name).join('.'); + } else if (uriStr != null) { + partOfUriStr = uriStr; + } + } + } + if (!hasDartCoreImport) { + imports.add( + UnlinkedNamespaceDirective( + configurations: [], + uri: 'dart:core', + ), + ); + } + + var declaredExtensions = []; + var declaredFunctions = []; + var declaredTypes = []; + var declaredVariables = []; + for (var declaration in unit.declarations) { + if (declaration is ClassDeclaration) { + declaredTypes.add(declaration.name.name); + } else if (declaration is EnumDeclaration) { + declaredTypes.add(declaration.name.name); + } else if (declaration is ExtensionDeclaration) { + var name = declaration.name; + if (name != null) { + declaredExtensions.add(name.name); + } + } else if (declaration is FunctionDeclaration) { + declaredFunctions.add(declaration.name.name); + } else if (declaration is MixinDeclaration) { + declaredTypes.add(declaration.name.name); + } else if (declaration is TopLevelVariableDeclaration) { + for (var variable in declaration.variables.variables) { + declaredVariables.add(variable.name.name); + } + } + } + + var unlinkedUnit = UnlinkedUnit( + apiSignature: computeUnlinkedApiSignature(unit), + exports: exports, + hasLibraryDirective: hasLibraryDirective, + hasPartOfDirective: hasPartOfDirective, + imports: imports, + informativeBytes: writeUnitInformative(unit), + lineStarts: Uint32List.fromList(unit.lineInfo!.lineStarts), + partOfName: partOfName, + partOfUri: partOfUriStr, + parts: parts, + ); + + return CiderUnlinkedUnit( + unit: unlinkedUnit, + topLevelDeclarations: CiderUnitTopLevelDeclarations( + extensionNames: declaredExtensions, + functionNames: declaredFunctions, + typeNames: declaredTypes, + variableNames: declaredVariables, + ), + ); + } + + static UnlinkedNamespaceDirective _serializeNamespaceDirective( + NamespaceDirective directive, + ) { + return UnlinkedNamespaceDirective( + configurations: directive.configurations.map((configuration) { + var name = configuration.name.components.join('.'); + var value = configuration.value?.stringValue ?? ''; + return UnlinkedNamespaceDirectiveConfiguration( + name: name, + value: value, + uri: configuration.uri.stringValue ?? '', + ); + }).toList(), + uri: directive.uri.stringValue ?? '', + ); + } +} + /// Node in [_LibraryWalker]. class _LibraryNode extends graph.Node<_LibraryNode> { final _LibraryWalker walker; @@ -979,7 +1098,7 @@ class _LibraryNode extends graph.Node<_LibraryNode> { @override List<_LibraryNode> computeDependencies() { - return file.directReferencedLibraries.map(walker.getNode).toList(); + return file.files().directReferencedLibraries.map(walker.getNode).toList(); } } @@ -1013,8 +1132,8 @@ class _LibraryWalker extends graph.DependencyWalker<_LibraryNode> { // Append direct referenced cycles. for (var node in scc) { var file = node.file; - _appendDirectlyReferenced(cycle, signature, file.importedFiles); - _appendDirectlyReferenced(cycle, signature, file.exportedFiles); + _appendDirectlyReferenced(cycle, signature, file.files().imported); + _appendDirectlyReferenced(cycle, signature, file.files().exported); } // Fill the cycle with libraries. @@ -1023,8 +1142,8 @@ class _LibraryWalker extends graph.DependencyWalker<_LibraryNode> { signature.addString(node.file.uriStr); - signature.addInt(node.file.libraryFiles.length); - for (var file in node.file.libraryFiles) { + signature.addInt(node.file.files().ofLibrary.length); + for (var file in node.file.files().ofLibrary) { signature.addBool(file.exists); signature.addBytes(file.apiSignature); } diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart index 7ff94e7c2c6..89c1fe00f14 100644 --- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart +++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart @@ -430,7 +430,7 @@ class FileResolver { var libraryFile = file; var partOfLibrary = file.partOfLibrary; if (partOfLibrary != null) { - if (partOfLibrary.libraryFiles.contains(file)) { + if (partOfLibrary.files().ofLibrary.contains(file)) { libraryFile = partOfLibrary; } } @@ -477,7 +477,7 @@ class FileResolver { var libraryFile = file; var partOfLibrary = file.partOfLibrary; if (partOfLibrary != null) { - if (partOfLibrary.libraryFiles.contains(file)) { + if (partOfLibrary.files().ofLibrary.contains(file)) { libraryFile = partOfLibrary; } } @@ -509,7 +509,7 @@ class FileResolver { libraryContext!.elementFactory, contextObjects!.inheritanceManager, libraryFile, - (file) => file.getContentWithSameDigest(), + (file) => file.getContent(), ); try { @@ -522,7 +522,7 @@ class FileResolver { }); } catch (exception, stackTrace) { var fileContentMap = {}; - for (var file in libraryFile.libraryFiles) { + for (var file in libraryFile.files().ofLibrary) { var path = file.path; fileContentMap[path] = _getFileContent(path); } @@ -543,7 +543,7 @@ class FileResolver { file.exists, file.getContent(), file.lineInfo, - file.unlinked.unit.hasPartOfDirective, + file.unlinkedUnit.hasPartOfDirective, fileResult.unit, fileResult.errors, ); @@ -822,8 +822,8 @@ class _LibraryContext { var unitsInformativeBytes = {}; for (var library in cycle.libraries) { - for (var file in library.libraryFiles) { - var informativeBytes = file.unlinked.unit.informativeBytes; + for (var file in library.files().ofLibrary) { + var informativeBytes = file.unlinkedUnit.informativeBytes; unitsInformativeBytes[file.uri] = informativeBytes; } } @@ -838,10 +838,10 @@ class _LibraryContext { var inputUnits = []; var partIndex = -1; - for (var file in libraryFile.libraryFiles) { + for (var file in libraryFile.files().ofLibrary) { var isSynthetic = !file.exists; - var content = file.getContentWithSameDigest(); + var content = file.getContent(); performance.getDataInt('parseCount').increment(); performance.getDataInt('parseLength').add(content.length); @@ -852,7 +852,7 @@ class _LibraryContext { String? partUriStr; if (partIndex >= 0) { - partUriStr = libraryFile.unlinked.unit.parts[partIndex]; + partUriStr = libraryFile.unlinkedUnit.parts[partIndex]; } partIndex++; diff --git a/pkg/analyzer/test/src/dart/micro/file_resolution.dart b/pkg/analyzer/test/src/dart/micro/file_resolution.dart index 583646540c0..52969ecd42f 100644 --- a/pkg/analyzer/test/src/dart/micro/file_resolution.dart +++ b/pkg/analyzer/test/src/dart/micro/file_resolution.dart @@ -14,6 +14,7 @@ import 'package:analyzer/src/test_utilities/find_element.dart'; import 'package:analyzer/src/test_utilities/find_node.dart'; import 'package:analyzer/src/test_utilities/mock_sdk.dart'; import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart'; +import 'package:analyzer/src/util/performance/operation_performance.dart'; import 'package:analyzer/src/workspace/bazel.dart'; import 'package:crypto/crypto.dart'; import 'package:linter/src/rules.dart'; @@ -71,8 +72,14 @@ class FileResolutionTest with ResourceProviderMixin, ResolutionTest { } @override - Future resolveFile(String path) async { - result = fileResolver.resolve(path: path); + Future resolveFile( + String path, { + OperationPerformanceImpl? performance, + }) async { + result = fileResolver.resolve( + path: path, + performance: performance, + ); return result; }