blob: c54627b5a9609d959885f89cdec5f4e377a13941 [file] [log] [blame]
// 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.
Uint8List buildSdkSummary({
required ResourceProvider resourceProvider,
required String sdkPath,
String? embedderYamlPath,
}) {
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.parent2: 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.
Uint8List build() {
librarySources.forEach(_addLibrary);
var elementFactory = LinkedElementFactory(
context,
AnalysisSessionImpl(
_FakeAnalysisDriver(),
),
Reference.root(),
);
var linkResult = link(elementFactory, inputLibraries, false);
var bundleBuilder = PackageBundleBuilder();
for (var library in inputLibraries) {
bundleBuilder.addLibrary(
library.uriStr,
library.units.map((e) => e.uriStr).toList(),
);
}
return bundleBuilder.finish(
astBytes: linkResult.astBytes,
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.tmp1(
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.tmp1(
partUriStr: partUri,
partDirectiveIndex: partDirectiveIndex++,
source: partSource,
isSynthetic: false,
unit: partUnit,
),
);
}
}
inputLibraries.add(
LinkInputLibrary.tmp1(
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);
}