| // Copyright (c) 2020, 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:typed_data'; |
| |
| import 'package:_fe_analyzer_shared/src/sdk/allowed_experiments.dart'; |
| import 'package:analyzer/dart/analysis/declared_variables.dart'; |
| import 'package:analyzer/dart/analysis/features.dart'; |
| import 'package:analyzer/dart/analysis/utilities.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/file_system/file_system.dart'; |
| import 'package:analyzer/src/context/context.dart'; |
| import 'package:analyzer/src/dart/analysis/driver.dart'; |
| import 'package:analyzer/src/dart/analysis/session.dart'; |
| import 'package:analyzer/src/dart/ast/ast.dart'; |
| import 'package:analyzer/src/dart/sdk/sdk.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:analyzer/src/summary2/link.dart'; |
| import 'package:analyzer/src/summary2/linked_element_factory.dart'; |
| import 'package:analyzer/src/summary2/package_bundle_format.dart'; |
| import 'package:analyzer/src/summary2/reference.dart'; |
| import 'package:pub_semver/pub_semver.dart'; |
| import 'package:yaml/yaml.dart'; |
| |
| /// Build summary for SDK at the given [sdkPath]. |
| /// |
| /// If [embedderYamlPath] is provided, then libraries from this file are |
| /// appended to the libraries of the specified SDK. |
| Future<Uint8List> buildSdkSummary2({ |
| required ResourceProvider resourceProvider, |
| required String sdkPath, |
| String? embedderYamlPath, |
| }) async { |
| var sdk = FolderBasedDartSdk( |
| resourceProvider, |
| resourceProvider.getFolder(sdkPath), |
| ); |
| |
| // Append libraries from the embedder. |
| if (embedderYamlPath != null) { |
| var file = resourceProvider.getFile(embedderYamlPath); |
| var content = file.readAsStringSync(); |
| var map = loadYaml(content) as YamlMap; |
| var embedderSdk = EmbedderSdk( |
| resourceProvider, |
| {file.parent: map}, |
| languageVersion: sdk.languageVersion, |
| ); |
| for (var library in embedderSdk.sdkLibraries) { |
| var uriStr = library.shortName; |
| if (sdk.libraryMap.getLibrary(uriStr) == null) { |
| sdk.libraryMap.setLibrary(uriStr, library); |
| } |
| } |
| } |
| |
| var librarySources = sdk.sdkLibraries.map((e) { |
| return sdk.mapDartUri(e.shortName)!; |
| }).toList(); |
| |
| var analysisContext = AnalysisContextImpl( |
| SynchronousSession(AnalysisOptionsImpl(), DeclaredVariables()), |
| SourceFactory([DartUriResolver(sdk)]), |
| ); |
| |
| return _Builder( |
| analysisContext, |
| sdk.allowedExperimentsJson, |
| sdk.languageVersion, |
| librarySources, |
| ).build(); |
| } |
| |
| class _Builder { |
| final AnalysisContextImpl context; |
| final String allowedExperimentsJson; |
| final Iterable<Source> librarySources; |
| |
| final Set<String> libraryUris = <String>{}; |
| final List<LinkInputLibrary> inputLibraries = []; |
| |
| late final AllowedExperiments allowedExperiments; |
| Version languageVersion; |
| |
| _Builder( |
| this.context, |
| this.allowedExperimentsJson, |
| this.languageVersion, |
| this.librarySources, |
| ) { |
| allowedExperiments = parseAllowedExperiments(allowedExperimentsJson); |
| } |
| |
| /// Build the linked bundle and return its bytes. |
| Future<Uint8List> build() async { |
| librarySources.forEach(_addLibrary); |
| |
| var elementFactory = LinkedElementFactory( |
| context, |
| AnalysisSessionImpl( |
| _FakeAnalysisDriver(), |
| ), |
| Reference.root(), |
| ); |
| |
| var linkResult = await link(elementFactory, inputLibraries); |
| |
| var bundleBuilder = PackageBundleBuilder(); |
| for (var library in inputLibraries) { |
| bundleBuilder.addLibrary( |
| library.uriStr, |
| library.units.map((e) => e.uriStr).toList(), |
| ); |
| } |
| return bundleBuilder.finish( |
| resolutionBytes: linkResult.resolutionBytes, |
| sdk: PackageBundleSdk( |
| languageVersionMajor: languageVersion.major, |
| languageVersionMinor: languageVersion.minor, |
| allowedExperimentsJson: allowedExperimentsJson, |
| ), |
| ); |
| } |
| |
| void _addLibrary(Source source) { |
| String uriStr = source.uri.toString(); |
| if (!libraryUris.add(uriStr)) { |
| return; |
| } |
| |
| var inputUnits = <LinkInputUnit>[]; |
| |
| CompilationUnit definingUnit = _parse(source); |
| inputUnits.add( |
| LinkInputUnit( |
| partDirectiveIndex: null, |
| source: source, |
| isSynthetic: false, |
| unit: definingUnit, |
| ), |
| ); |
| |
| var partDirectiveIndex = 0; |
| for (Directive directive in definingUnit.directives) { |
| if (directive is NamespaceDirective) { |
| String libUri = directive.uri.stringValue!; |
| Source libSource = context.sourceFactory.resolveUri(source, libUri)!; |
| _addLibrary(libSource); |
| } else if (directive is PartDirective) { |
| String partUri = directive.uri.stringValue!; |
| Source partSource = context.sourceFactory.resolveUri(source, partUri)!; |
| CompilationUnit partUnit = _parse(partSource); |
| inputUnits.add( |
| LinkInputUnit( |
| partUriStr: partUri, |
| partDirectiveIndex: partDirectiveIndex++, |
| source: partSource, |
| isSynthetic: false, |
| unit: partUnit, |
| ), |
| ); |
| } |
| } |
| |
| inputLibraries.add( |
| LinkInputLibrary( |
| source: source, |
| units: inputUnits, |
| ), |
| ); |
| } |
| |
| /// Return the [FeatureSet] for the given [uri], must be a `dart:` URI. |
| FeatureSet _featureSet(Uri uri) { |
| if (uri.isScheme('dart')) { |
| var pathSegments = uri.pathSegments; |
| if (pathSegments.isNotEmpty) { |
| var libraryName = pathSegments.first; |
| var experiments = allowedExperiments.forSdkLibrary(libraryName); |
| return FeatureSet.fromEnableFlags2( |
| sdkLanguageVersion: languageVersion, |
| flags: experiments, |
| ); |
| } |
| } |
| throw StateError('Expected a valid dart: URI: $uri'); |
| } |
| |
| String _getContent(Source source) { |
| var uriStr = '${source.uri}'; |
| var content = source.contents.data; |
| |
| // https://github.com/google/json_serializable.dart/issues/692 |
| // SDK 2.9 was released with the syntax that we later decided to remove. |
| // But the current analyzer still says that it supports SDK 2.9, so we |
| // have to be able to handle this code. We do this by rewriting it into |
| // the syntax that we support now. |
| if (uriStr == 'dart:core/uri.dart') { |
| return content.replaceAll( |
| 'String? charsetName = parameters?.["charset"];', |
| 'String? charsetName = parameters? ["charset"];', |
| ); |
| } |
| if (uriStr == 'dart:_http/http_headers.dart') { |
| return content.replaceAll( |
| 'return _originalHeaderNames?.[name] ?? name;', |
| 'return _originalHeaderNames? [name] ?? name;', |
| ); |
| } |
| |
| return content; |
| } |
| |
| CompilationUnit _parse(Source source) { |
| var result = parseString( |
| content: _getContent(source), |
| featureSet: _featureSet(source.uri), |
| throwIfDiagnostics: false, |
| path: source.fullName, |
| ); |
| |
| if (result.errors.isNotEmpty) { |
| var errorsStr = result.errors.map((e) { |
| var location = result.lineInfo.getLocation(e.offset); |
| return '${source.fullName}:$location - ${e.message}'; |
| }).join('\n'); |
| throw StateError( |
| 'Unexpected diagnostics:\n$errorsStr', |
| ); |
| } |
| |
| var unit = result.unit as CompilationUnitImpl; |
| unit.languageVersion = LibraryLanguageVersion( |
| package: languageVersion, |
| override: null, |
| ); |
| |
| return result.unit; |
| } |
| } |
| |
| class _FakeAnalysisDriver implements AnalysisDriver { |
| @override |
| noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); |
| } |