| // Copyright (c) 2016, 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 'dart:io' as io; |
| |
| import 'package:analyzer/dart/analysis/features.dart'; |
| import 'package:analyzer/dart/analysis/utilities.dart'; |
| import 'package:analyzer/exception/exception.dart'; |
| import 'package:analyzer/file_system/file_system.dart'; |
| import 'package:analyzer/file_system/memory_file_system.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/java_engine_io.dart'; |
| import 'package:analyzer/src/generated/sdk.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:pub_semver/pub_semver.dart'; |
| import 'package:yaml/yaml.dart'; |
| |
| Version languageVersionFromSdkVersion(String sdkVersionStr) { |
| var sdkVersionParts = sdkVersionStr.split('.'); |
| var sdkVersionMajor = int.parse(sdkVersionParts[0]); |
| var sdkVersionMinor = int.parse(sdkVersionParts[1]); |
| return Version(sdkVersionMajor, sdkVersionMinor, 0); |
| } |
| |
| /// An abstract implementation of a Dart SDK in which the available libraries |
| /// are stored in a library map. Subclasses are responsible for populating the |
| /// library map. |
| abstract class AbstractDartSdk implements DartSdk { |
| /// The resource provider used to access the file system. |
| late final ResourceProvider resourceProvider; |
| |
| /// A mapping from Dart library URI's to the library represented by that URI. |
| LibraryMap libraryMap = LibraryMap(); |
| |
| /// The mapping from Dart URI's to the corresponding sources. |
| final Map<String, Source?> _uriToSourceMap = HashMap<String, Source?>(); |
| |
| @override |
| List<SdkLibraryImpl> get sdkLibraries => libraryMap.sdkLibraries; |
| |
| /// Return the path separator used by the resource provider. |
| String get separator => resourceProvider.pathContext.separator; |
| |
| @override |
| List<String> get uris => libraryMap.uris; |
| |
| /// Return info for debugging https://github.com/dart-lang/sdk/issues/35226. |
| Map<String, Object> debugInfo() { |
| return <String, Object>{ |
| 'runtimeType': '$runtimeType', |
| 'libraryMap': libraryMap.debugInfo(), |
| }; |
| } |
| |
| @override |
| Source? fromFileUri(Uri uri) { |
| File file = |
| resourceProvider.getFile(resourceProvider.pathContext.fromUri(uri)); |
| String? path = _getPath(file); |
| if (path == null) { |
| return null; |
| } |
| try { |
| return file.createSource(Uri.parse(path)); |
| } on FormatException catch (exception, stackTrace) { |
| AnalysisEngine.instance.instrumentationService.logInfo( |
| "Failed to create URI: $path", |
| CaughtException(exception, stackTrace)); |
| } |
| return null; |
| } |
| |
| String? getRelativePathFromFile(File file); |
| |
| @override |
| SdkLibrary? getSdkLibrary(String dartUri) => libraryMap.getLibrary(dartUri); |
| |
| Source? internalMapDartUri(String dartUri) { |
| // TODO(brianwilkerson) Figure out how to unify the implementations in the |
| // two subclasses. |
| String libraryName; |
| String relativePath; |
| int index = dartUri.indexOf('/'); |
| if (index >= 0) { |
| libraryName = dartUri.substring(0, index); |
| relativePath = dartUri.substring(index + 1); |
| } else { |
| libraryName = dartUri; |
| relativePath = ""; |
| } |
| SdkLibrary? library = getSdkLibrary(libraryName); |
| if (library == null) { |
| return null; |
| } |
| String srcPath; |
| if (relativePath.isEmpty) { |
| srcPath = library.path; |
| } else { |
| String libraryPath = library.path; |
| int index = libraryPath.lastIndexOf(separator); |
| if (index == -1) { |
| index = libraryPath.lastIndexOf('/'); |
| if (index == -1) { |
| return null; |
| } |
| } |
| String prefix = libraryPath.substring(0, index + 1); |
| srcPath = '$prefix$relativePath'; |
| } |
| String filePath = srcPath.replaceAll('/', separator); |
| try { |
| File file = resourceProvider.getFile(filePath); |
| return file.createSource(Uri.parse(dartUri)); |
| } on FormatException { |
| return null; |
| } |
| } |
| |
| @override |
| Source? mapDartUri(String dartUri) { |
| Source? source = _uriToSourceMap[dartUri]; |
| if (source == null) { |
| source = internalMapDartUri(dartUri); |
| _uriToSourceMap[dartUri] = source; |
| } |
| return source; |
| } |
| |
| @override |
| Uri? pathToUri(String path) { |
| var file = resourceProvider.getFile(path); |
| |
| var uriStr = _getPath(file); |
| if (uriStr == null) { |
| return null; |
| } |
| |
| try { |
| return Uri.parse(uriStr); |
| } on FormatException { |
| return null; |
| } |
| } |
| |
| /// TODO(scheglov) This name is misleading, returns `dart:foo/bar.dart`. |
| String? _getPath(File file) { |
| List<SdkLibrary> libraries = libraryMap.sdkLibraries; |
| int length = libraries.length; |
| String? filePath = getRelativePathFromFile(file); |
| if (filePath == null) { |
| return null; |
| } |
| List<String> paths = <String>[]; |
| for (int i = 0; i < length; i++) { |
| SdkLibrary library = libraries[i]; |
| String libraryPath = library.path.replaceAll('/', separator); |
| if (filePath == libraryPath) { |
| return library.shortName; |
| } |
| paths.add(libraryPath); |
| } |
| for (int i = 0; i < length; i++) { |
| SdkLibrary library = libraries[i]; |
| String libraryPath = paths[i]; |
| int index = libraryPath.lastIndexOf(separator); |
| if (index >= 0) { |
| String prefix = libraryPath.substring(0, index + 1); |
| if (filePath.startsWith(prefix)) { |
| String relPath = |
| filePath.substring(prefix.length).replaceAll(separator, '/'); |
| return '${library.shortName}/$relPath'; |
| } |
| } |
| } |
| return null; |
| } |
| } |
| |
| /// An SDK backed by URI mappings derived from an `_embedder.yaml` file. |
| class EmbedderSdk extends AbstractDartSdk { |
| static const String _DART_COLON_PREFIX = 'dart:'; |
| |
| static const String _EMBEDDED_LIB_MAP_KEY = 'embedded_libs'; |
| |
| late final Version _languageVersion; |
| |
| final Map<String, String> _urlMappings = HashMap<String, String>(); |
| |
| /// TODO(scheglov) Make [languageVersion] required. |
| /// https://github.com/dart-lang/sdk/issues/42890 |
| EmbedderSdk( |
| ResourceProvider resourceProvider, |
| Map<Folder, YamlMap>? embedderYamls, { |
| Version? languageVersion, |
| }) { |
| this.resourceProvider = resourceProvider; |
| _languageVersion = |
| languageVersion ?? languageVersionFromSdkVersion(io.Platform.version); |
| embedderYamls?.forEach(_processEmbedderYaml); |
| } |
| |
| @override |
| String? get allowedExperimentsJson { |
| var coreSource = mapDartUri('dart:core'); |
| if (coreSource != null) { |
| var coreFile = resourceProvider.getFile(coreSource.fullName); |
| var embeddedFolder = coreFile.parent.parent; |
| try { |
| return embeddedFolder |
| .getChildAssumingFolder('_internal') |
| .getChildAssumingFile('allowed_experiments.json') |
| .readAsStringSync(); |
| } catch (_) {} |
| } |
| return null; |
| } |
| |
| @override |
| Version get languageVersion => _languageVersion; |
| |
| @override |
| // TODO(danrubel) Determine SDK version |
| String get sdkVersion => '0'; |
| |
| /// The url mappings for this SDK. |
| Map<String, String> get urlMappings => _urlMappings; |
| |
| @override |
| String getRelativePathFromFile(File file) => file.path; |
| |
| @override |
| Source? internalMapDartUri(String dartUri) { |
| String libraryName; |
| String relativePath; |
| int index = dartUri.indexOf('/'); |
| if (index >= 0) { |
| libraryName = dartUri.substring(0, index); |
| relativePath = dartUri.substring(index + 1); |
| } else { |
| libraryName = dartUri; |
| relativePath = ""; |
| } |
| SdkLibrary? library = getSdkLibrary(libraryName); |
| if (library == null) { |
| return null; |
| } |
| String srcPath; |
| if (relativePath.isEmpty) { |
| srcPath = library.path; |
| } else { |
| String libraryPath = library.path; |
| int index = libraryPath.lastIndexOf(separator); |
| if (index == -1) { |
| index = libraryPath.lastIndexOf('/'); |
| if (index == -1) { |
| return null; |
| } |
| } |
| String prefix = libraryPath.substring(0, index + 1); |
| srcPath = '$prefix$relativePath'; |
| } |
| String filePath = srcPath.replaceAll('/', separator); |
| try { |
| File file = resourceProvider.getFile(filePath); |
| return file.createSource(Uri.parse(dartUri)); |
| } on FormatException { |
| return null; |
| } |
| } |
| |
| /// Install the mapping from [name] to [libDir]/[file]. |
| void _processEmbeddedLibs(String name, String file, Folder libDir) { |
| if (!name.startsWith(_DART_COLON_PREFIX)) { |
| // SDK libraries must begin with 'dart:'. |
| return; |
| } |
| String libPath = libDir.canonicalizePath(file); |
| _urlMappings[name] = libPath; |
| SdkLibraryImpl library = SdkLibraryImpl(name); |
| library.path = libPath; |
| libraryMap.setLibrary(name, library); |
| } |
| |
| /// Given the 'embedderYamls' from [EmbedderYamlLocator] check each one for |
| /// the top level key 'embedded_libs'. Under the 'embedded_libs' key are key |
| /// value pairs. Each key is a 'dart:' library uri and each value is a path |
| /// (relative to the directory containing `_embedder.yaml`) to a dart script |
| /// for the given library. For example: |
| /// |
| /// embedded_libs: |
| /// 'dart:io': '../../sdk/io/io.dart' |
| /// |
| /// If a key doesn't begin with `dart:` it is ignored. |
| void _processEmbedderYaml(Folder libDir, YamlMap map) { |
| YamlNode embeddedLibs = map[_EMBEDDED_LIB_MAP_KEY]; |
| if (embeddedLibs is YamlMap) { |
| embeddedLibs.forEach((k, v) => _processEmbeddedLibs(k, v, libDir)); |
| } |
| } |
| } |
| |
| /// A Dart SDK installed in a specified directory. Typical Dart SDK layout is |
| /// something like... |
| /// |
| /// dart-sdk/ |
| /// bin/ |
| /// dart[.exe] <-- VM |
| /// lib/ |
| /// core/ |
| /// core.dart |
| /// ... other core library files ... |
| /// ... other libraries ... |
| /// util/ |
| /// ... Dart utilities ... |
| /// Chromium/ <-- Dartium typically exists in a sibling directory |
| class FolderBasedDartSdk extends AbstractDartSdk { |
| /// The name of the directory within the SDK directory that contains |
| /// executables. |
| static const String _BIN_DIRECTORY_NAME = "bin"; |
| |
| /// The name of the directory within the SDK directory that contains |
| /// documentation for the libraries. |
| static const String _DOCS_DIRECTORY_NAME = "docs"; |
| |
| /// The name of the directory within the SDK directory that contains the |
| /// sdk_library_metadata directory. |
| static const String _INTERNAL_DIR = "_internal"; |
| |
| /// The name of the sdk_library_metadata directory that contains the package |
| /// holding the libraries.dart file. |
| static const String _SDK_LIBRARY_METADATA_DIR = "sdk_library_metadata"; |
| |
| /// The name of the directory within the sdk_library_metadata that contains |
| /// libraries.dart. |
| static const String _SDK_LIBRARY_METADATA_LIB_DIR = "lib"; |
| |
| /// The name of the directory within the SDK directory that contains the |
| /// libraries. |
| static const String _LIB_DIRECTORY_NAME = "lib"; |
| |
| /// The name of the libraries file. |
| static const String _LIBRARIES_FILE = "libraries.dart"; |
| |
| /// The name of the pub executable on windows. |
| static const String _PUB_EXECUTABLE_NAME_WIN = "pub.bat"; |
| |
| /// The name of the pub executable on non-windows operating systems. |
| static const String _PUB_EXECUTABLE_NAME = "pub"; |
| |
| /// The name of the file within the SDK directory that contains the version |
| /// number of the SDK. |
| static const String _VERSION_FILE_NAME = "version"; |
| |
| /// The directory containing the SDK. |
| final Folder _sdkDirectory; |
| |
| /// The directory within the SDK directory that contains the libraries. |
| Folder? _libraryDirectory; |
| |
| /// The revision number of this SDK, or `"0"` if the revision number cannot be |
| /// discovered. |
| String? _sdkVersion; |
| |
| /// The cached language version of this SDK. |
| Version? _languageVersion; |
| |
| /// The file containing the pub executable. |
| File? _pubExecutable; |
| |
| /// Initialize a newly created SDK to represent the Dart SDK installed in the |
| /// [sdkDirectory]. |
| FolderBasedDartSdk(ResourceProvider resourceProvider, Folder sdkDirectory) |
| : _sdkDirectory = sdkDirectory { |
| this.resourceProvider = resourceProvider; |
| libraryMap = initialLibraryMap(); |
| } |
| |
| @override |
| String get allowedExperimentsJson { |
| return _sdkDirectory |
| .getChildAssumingFolder('lib') |
| .getChildAssumingFolder('_internal') |
| .getChildAssumingFile('allowed_experiments.json') |
| .readAsStringSync(); |
| } |
| |
| /// Return the directory containing the SDK. |
| Folder get directory => _sdkDirectory; |
| |
| /// Return the directory containing documentation for the SDK. |
| Folder get docDirectory => |
| _sdkDirectory.getChildAssumingFolder(_DOCS_DIRECTORY_NAME); |
| |
| @override |
| Version get languageVersion { |
| if (_languageVersion == null) { |
| var sdkVersionStr = _sdkDirectory |
| .getChildAssumingFile(_VERSION_FILE_NAME) |
| .readAsStringSync(); |
| _languageVersion = languageVersionFromSdkVersion(sdkVersionStr); |
| } |
| |
| return _languageVersion!; |
| } |
| |
| /// Return the directory within the SDK directory that contains the libraries. |
| Folder get libraryDirectory { |
| return _libraryDirectory ??= |
| _sdkDirectory.getChildAssumingFolder(_LIB_DIRECTORY_NAME); |
| } |
| |
| /// Return the file containing the Pub executable, or `null` if it does not |
| /// exist. |
| File get pubExecutable { |
| return _pubExecutable ??= _sdkDirectory |
| .getChildAssumingFolder(_BIN_DIRECTORY_NAME) |
| .getChildAssumingFile(OSUtilities.isWindows() |
| ? _PUB_EXECUTABLE_NAME_WIN |
| : _PUB_EXECUTABLE_NAME); |
| } |
| |
| /// Return the revision number of this SDK, or `"0"` if the revision number |
| /// cannot be discovered. |
| @override |
| String get sdkVersion { |
| if (_sdkVersion == null) { |
| File revisionFile = |
| _sdkDirectory.getChildAssumingFile(_VERSION_FILE_NAME); |
| try { |
| String revision = revisionFile.readAsStringSync(); |
| _sdkVersion = revision.trim(); |
| } on FileSystemException { |
| return _sdkVersion = DartSdk.DEFAULT_VERSION; |
| } |
| } |
| return _sdkVersion!; |
| } |
| |
| /// Determine the search order for trying to locate the [_LIBRARIES_FILE]. |
| Iterable<File> get _libraryMapLocations sync* { |
| yield libraryDirectory |
| .getChildAssumingFolder(_INTERNAL_DIR) |
| .getChildAssumingFolder(_SDK_LIBRARY_METADATA_DIR) |
| .getChildAssumingFolder(_SDK_LIBRARY_METADATA_LIB_DIR) |
| .getChildAssumingFile(_LIBRARIES_FILE); |
| yield libraryDirectory |
| .getChildAssumingFolder(_INTERNAL_DIR) |
| .getChildAssumingFile(_LIBRARIES_FILE); |
| } |
| |
| /// Return info for debugging https://github.com/dart-lang/sdk/issues/35226. |
| @override |
| Map<String, Object> debugInfo() { |
| var result = super.debugInfo(); |
| result['directory'] = _sdkDirectory.path; |
| return result; |
| } |
| |
| @override |
| String? getRelativePathFromFile(File file) { |
| String filePath = file.path; |
| String libPath = libraryDirectory.path; |
| if (!filePath.startsWith("$libPath$separator")) { |
| return null; |
| } |
| return filePath.substring(libPath.length + 1); |
| } |
| |
| /// Read all of the configuration files to initialize the library maps. |
| /// Return the initialized library map. |
| LibraryMap initialLibraryMap() { |
| List<String> searchedPaths = <String>[]; |
| late StackTrace lastStackTrace; |
| late Object lastException; |
| for (File librariesFile in _libraryMapLocations) { |
| try { |
| String contents = librariesFile.readAsStringSync(); |
| return SdkLibrariesReader().readFromFile(librariesFile, contents); |
| } catch (exception, stackTrace) { |
| print('[exception: $exception][stackTrace: $stackTrace]'); |
| searchedPaths.add(librariesFile.path); |
| lastException = exception; |
| lastStackTrace = stackTrace; |
| } |
| } |
| StringBuffer buffer = StringBuffer(); |
| buffer.writeln('Could not initialize the library map from $searchedPaths'); |
| if (resourceProvider is MemoryResourceProvider) { |
| (resourceProvider as MemoryResourceProvider).writeOn(buffer); |
| } |
| // TODO(39284): should this exception be silent? |
| AnalysisEngine.instance.instrumentationService.logException( |
| SilentException(buffer.toString(), lastException, lastStackTrace)); |
| return LibraryMap(); |
| } |
| |
| @override |
| Source? internalMapDartUri(String dartUri) { |
| String libraryName; |
| String relativePath; |
| int index = dartUri.indexOf('/'); |
| if (index >= 0) { |
| libraryName = dartUri.substring(0, index); |
| relativePath = dartUri.substring(index + 1); |
| } else { |
| libraryName = dartUri; |
| relativePath = ""; |
| } |
| SdkLibrary? library = getSdkLibrary(libraryName); |
| if (library == null) { |
| return null; |
| } |
| try { |
| File file = libraryDirectory.getChildAssumingFile(library.path); |
| if (relativePath.isNotEmpty) { |
| File relativeFile = file.parent.getChildAssumingFile(relativePath); |
| if (relativeFile.path == file.path) { |
| // The relative file is the library, so return a Source for the |
| // library rather than the part format. |
| return file.createSource(Uri.parse(library.shortName)); |
| } |
| file = relativeFile; |
| } |
| return file.createSource(Uri.parse(dartUri)); |
| } on FormatException { |
| return null; |
| } |
| } |
| } |
| |
| /// An object used to read and parse the libraries file |
| /// (dart-sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart) for |
| /// information about the libraries in an SDK. The library information is |
| /// represented as a Dart file containing a single top-level variable whose |
| /// value is a const map. The keys of the map are the names of libraries defined |
| /// in the SDK and the values in the map are info objects defining the library. |
| /// For example, a subset of a typical SDK might have a libraries file that |
| /// looks like the following: |
| /// |
| /// final Map<String, LibraryInfo> LIBRARIES = const <LibraryInfo> { |
| /// // Used by VM applications |
| /// "builtin" : const LibraryInfo( |
| /// "builtin/builtin_runtime.dart", |
| /// category: "Server", |
| /// platforms: VM_PLATFORM), |
| /// |
| /// "compiler" : const LibraryInfo( |
| /// "compiler/compiler.dart", |
| /// category: "Tools", |
| /// platforms: 0), |
| /// }; |
| class SdkLibrariesReader { |
| /// Return the library map read from the given [file], given that the content |
| /// of the file is already known to be [libraryFileContents]. |
| LibraryMap readFromFile(File file, String libraryFileContents) => |
| readFromSource(file.createSource(), libraryFileContents); |
| |
| /// Return the library map read from the given [source], given that the |
| /// content of the file is already known to be [libraryFileContents]. |
| LibraryMap readFromSource(Source source, String libraryFileContents) { |
| // TODO(paulberry): initialize the feature set appropriately based on the |
| // version of the SDK we are reading, and enable flags. |
| var featureSet = FeatureSet.latestLanguageVersion(); |
| |
| var parseResult = parseString( |
| content: libraryFileContents, |
| featureSet: featureSet, |
| throwIfDiagnostics: false, |
| path: source.fullName, |
| ); |
| var unit = parseResult.unit; |
| |
| var libraryBuilder = SdkLibrariesReader_LibraryBuilder(); |
| if (parseResult.errors.isEmpty) { |
| unit.accept(libraryBuilder); |
| } |
| return libraryBuilder.librariesMap; |
| } |
| } |