blob: 1e1b2c9548c60fe5ad9d7c4fe1df461e19d865b3 [file]
// Copyright (c) 2018, 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:async';
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/analysis_options/analysis_options.dart';
import 'package:analyzer/src/analysis_options/analysis_options_parser.dart';
import 'package:analyzer/src/context/builder.dart' show locateEmbedderYamlFor;
import 'package:analyzer/src/context/packages.dart';
import 'package:analyzer/src/dart/analysis/analysis_options_map.dart';
import 'package:analyzer/src/dart/analysis/byte_store.dart'
show ByteStore, MemoryByteStore;
import 'package:analyzer/src/dart/analysis/context_root.dart';
import 'package:analyzer/src/dart/analysis/driver.dart'
show
AnalysisDriver,
AnalysisDriverScheduler,
AnalysisDriverTestView,
OwnedFiles;
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
import 'package:analyzer/src/dart/analysis/file_content_cache.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart'
show PerformanceLog;
import 'package:analyzer/src/dart/analysis/unlinked_unit_store.dart';
import 'package:analyzer/src/dart/sdk/sdk.dart';
import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
import 'package:analyzer/src/generated/source.dart' show SourceFactory;
import 'package:analyzer/src/summary/package_bundle_reader.dart';
import 'package:analyzer/src/summary/summary_sdk.dart';
import 'package:analyzer/src/summary2/package_bundle_format.dart';
import 'package:analyzer/src/workspace/workspace.dart';
/// A utility class used to build an analysis context based on a context root.
class ContextBuilderImpl {
/// The resource provider used to access the file system.
final ResourceProvider resourceProvider;
/// Analysis options mappings shared by all contexts built by this builder.
final AnalysisOptionsMap _optionsMap = AnalysisOptionsMap();
/// Initialize a newly created context builder. If a [resourceProvider] is
/// given, then it will be used to access the file system, otherwise the
/// default resource provider will be used.
ContextBuilderImpl({ResourceProvider? resourceProvider})
: resourceProvider = resourceProvider ?? PhysicalResourceProvider.INSTANCE;
/// Return an analysis context corresponding to the given [contextRoot].
///
/// If a set of [declaredVariables] is provided, the values will be used to
/// map the variable names found in `fromEnvironment` invocations to the
/// constant value that will be returned. If none is given, then no variables
/// will be defined.
///
/// If a list of [librarySummaryPaths] is provided, then the summary files at
/// those paths will be used, when possible, when analyzing the libraries
/// contained in the summary files.
///
/// If an [sdkPath] is provided, and if it is a valid path to a directory
/// containing a valid SDK, then the SDK in the referenced directory will be
/// used when analyzing the code in the context.
///
/// If an [sdkSummaryPath] is provided, then that file will be used as the
/// summary file for the SDK.
DriverBasedAnalysisContext createContext({
ByteStore? byteStore,
required ContextRootImpl contextRoot,
bool definedOptionsFile = false,
DeclaredVariables? declaredVariables,
bool drainStreams = true,
bool enableIndex = false,
List<String>? librarySummaryPaths,
PerformanceLog? performanceLog,
bool retainDataForTesting = false,
AnalysisDriverScheduler? scheduler,
required String sdkPath,
String? sdkSummaryPath,
void Function({required AnalysisOptionsImpl analysisOptions})?
updateAnalysisOptions4,
void Function({required AnalysisOptionsBuilder analysisOptionsBuilder})?
configureAnalysisOptionsBuilder,
FileContentCache? fileContentCache,
UnlinkedUnitStore? unlinkedUnitStore,
OwnedFiles? ownedFiles,
bool enableLintRuleTiming = false,
required bool withFineDependencies,
List<String> enabledExperiments = const [],
}) {
byteStore ??= MemoryByteStore();
performanceLog ??= PerformanceLog(null);
if (scheduler == null) {
scheduler = AnalysisDriverScheduler(performanceLog);
scheduler.start();
}
SummaryDataStore? summaryData;
if (librarySummaryPaths != null) {
summaryData = SummaryDataStore();
for (var summaryPath in librarySummaryPaths) {
var bytes = resourceProvider.getFile(summaryPath).readAsBytesSync();
var bundle = PackageBundleReader(bytes);
summaryData.addBundle(summaryPath, bundle);
}
}
var workspace = contextRoot.workspace;
var sdk = _createSdk(
workspace: workspace,
sdkPath: sdkPath,
sdkSummaryPath: sdkSummaryPath,
);
// TODO(scheglov): Ensure that "librarySummaryPaths" not null only
// when "sdkSummaryPath" is not null.
if (sdk is SummaryBasedDartSdk) {
summaryData?.addBundle(null, sdk.bundle);
}
var optionsFile = contextRoot.optionsFile;
var sourceFactory = workspace.createSourceFactory(sdk, summaryData);
var analysisOptionsParseSession = AnalysisOptionsParseSession();
AnalysisOptionsMap analysisOptionsMap;
// If there's an options file defined (as, e.g. passed into the
// AnalysisContextCollection), use a shared options map based on it.
if (definedOptionsFile && optionsFile != null) {
analysisOptionsMap = AnalysisOptionsMap.forSharedOptions(
_getAnalysisOptions(
optionsFile,
analysisOptionsParseSession,
sourceFactory,
contextRoot.root,
sdk,
updateAnalysisOptions4,
configureAnalysisOptionsBuilder,
enabledExperiments,
),
);
} else {
// Otherwise, create one from the options file mappings stored in the
// context root.
analysisOptionsMap = _createOptionsMap(
contextRoot,
analysisOptionsParseSession,
sourceFactory,
updateAnalysisOptions4,
configureAnalysisOptionsBuilder,
);
}
var analysisContext = DriverBasedAnalysisContext(
resourceProvider,
contextRoot,
);
var driver = AnalysisDriver(
scheduler: scheduler,
logger: performanceLog,
resourceProvider: resourceProvider,
byteStore: byteStore,
sourceFactory: sourceFactory,
analysisOptionsMap: analysisOptionsMap,
packages: _createPackageMap(contextRoot: contextRoot),
analysisContext: analysisContext,
enableIndex: enableIndex,
externalSummaries: summaryData,
retainDataForTesting: retainDataForTesting,
fileContentCache: fileContentCache,
unlinkedUnitStore: unlinkedUnitStore,
declaredVariables: declaredVariables,
testView: retainDataForTesting ? AnalysisDriverTestView() : null,
ownedFiles: ownedFiles,
enableLintRuleTiming: enableLintRuleTiming,
withFineDependencies: withFineDependencies,
);
// AnalysisDriver reports results into streams.
// We need to drain these streams to avoid memory leak.
if (drainStreams) {
unawaited(driver.exceptions.drain<void>());
}
return analysisContext;
}
/// Create an [AnalysisOptionsMap] for the given [contextRoot].
AnalysisOptionsMap _createOptionsMap(
ContextRootImpl contextRoot,
AnalysisOptionsParseSession analysisOptionsParseSession,
SourceFactory sourceFactory,
void Function({required AnalysisOptionsImpl analysisOptions})?
updateAnalysisOptions4,
void Function({required AnalysisOptionsBuilder analysisOptionsBuilder})?
configureAnalysisOptionsBuilder,
) {
var optionsMappings = contextRoot.optionsFileMap.entries;
for (var MapEntry(key: file, value: folders) in optionsMappings) {
var options = analysisOptionsParseSession
.parse(
sourceFactory: sourceFactory,
contextRoot: contextRoot.root,
file: file,
)
.analysisOptions;
options = _updatedAnalysisOptions(
options,
updateAnalysisOptions4,
configureAnalysisOptionsBuilder,
);
for (var folder in folders) {
_optionsMap[folder] = options;
}
}
if (_optionsMap.folders.isEmpty) {
return AnalysisOptionsMap.forSharedOptions(
_updatedAnalysisOptions(
AnalysisOptionsImpl(),
updateAnalysisOptions4,
configureAnalysisOptionsBuilder,
),
);
}
return _optionsMap;
}
/// Return [Packages] to analyze the [contextRoot].
///
// TODO(scheglov): Get [Packages] from [Workspace]?
Packages _createPackageMap({required ContextRootImpl contextRoot}) {
var packagesFile = contextRoot.packagesFile;
if (packagesFile != null) {
return parsePackageConfigJsonFile(resourceProvider, packagesFile);
} else {
return Packages.empty;
}
}
/// Return the SDK that should be used to analyze code.
DartSdk _createSdk({
required Workspace workspace,
required String sdkPath,
String? sdkSummaryPath,
}) {
if (sdkSummaryPath != null) {
var file = resourceProvider.getFile(sdkSummaryPath);
var bytes = file.readAsBytesSync();
return SummaryBasedDartSdk.forBundle(PackageBundleReader(bytes));
}
var folderSdk = FolderBasedDartSdk(
resourceProvider,
resourceProvider.getFolder(sdkPath),
);
{
var partialSourceFactory = workspace.partialSourceFactory;
var embedderYamlSource = partialSourceFactory.forUri(
'package:sky_engine/_embedder.yaml',
);
if (embedderYamlSource != null) {
var embedderYamlPath = embedderYamlSource.fullName;
var libFolder = resourceProvider.getFile(embedderYamlPath).parent;
var embedderYaml = locateEmbedderYamlFor(libFolder);
if (embedderYaml != null) {
return EmbedderSdk.new2(
resourceProvider,
libFolder,
embedderYaml,
languageVersion: folderSdk.languageVersion,
);
}
}
}
return folderSdk;
}
/// Return the analysis options from [optionsFile].
///
// TODO(scheglov): We have already loaded it once in [ContextLocatorImpl].
AnalysisOptionsImpl _getAnalysisOptions(
File optionsFile,
AnalysisOptionsParseSession analysisOptionsParseSession,
SourceFactory sourceFactory,
Folder contextRoot,
DartSdk sdk,
void Function({required AnalysisOptionsImpl analysisOptions})?
updateAnalysisOptions4,
void Function({required AnalysisOptionsBuilder analysisOptionsBuilder})?
configureAnalysisOptionsBuilder,
List<String> enabledExperiments,
) {
AnalysisOptionsImpl options;
try {
options = analysisOptionsParseSession
.parse(
sourceFactory: sourceFactory,
contextRoot: contextRoot,
file: optionsFile,
)
.analysisOptions;
} catch (e) {
// Ignore exception.
options = AnalysisOptionsImpl(file: optionsFile);
}
var analysisOptionsBuilder = AnalysisOptionsBuilder.from(options)
..contextFeatures = FeatureSet.fromEnableFlags2(
sdkLanguageVersion: sdk.languageVersion,
flags: enabledExperiments,
);
options = analysisOptionsBuilder.build();
if (updateAnalysisOptions4 != null) {
updateAnalysisOptions4(analysisOptions: options);
}
analysisOptionsBuilder = AnalysisOptionsBuilder.from(options);
if (configureAnalysisOptionsBuilder != null) {
configureAnalysisOptionsBuilder(
analysisOptionsBuilder: analysisOptionsBuilder,
);
}
return analysisOptionsBuilder.build();
}
AnalysisOptionsImpl _updatedAnalysisOptions(
AnalysisOptionsImpl options,
void Function({required AnalysisOptionsImpl analysisOptions})?
updateAnalysisOptions4,
void Function({required AnalysisOptionsBuilder analysisOptionsBuilder})?
configureAnalysisOptionsBuilder,
) {
if (updateAnalysisOptions4 != null) {
updateAnalysisOptions4(analysisOptions: options);
}
if (configureAnalysisOptionsBuilder == null) {
return options;
}
var analysisOptionsBuilder = AnalysisOptionsBuilder.from(options);
configureAnalysisOptionsBuilder(
analysisOptionsBuilder: analysisOptionsBuilder,
);
return analysisOptionsBuilder.build();
}
}